<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.prashantrajan.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.prashantrajan.com/" rel="alternate" type="text/html" /><updated>2024-12-28T05:54:54+00:00</updated><id>https://blog.prashantrajan.com/feed.xml</id><title type="html">@prashantrajan’s Blog</title><subtitle>Sharing insights on technology, software development, and technical leadership from 20+ years of experience—practical tips and thoughtful perspectives for developers and tech leaders alike.</subtitle><author><name>Prashant Nadarajan</name></author><entry><title type="html">Internet Startup Security Checklist</title><link href="https://blog.prashantrajan.com/2014/04/20/internet-startup-security-checklist.html" rel="alternate" type="text/html" title="Internet Startup Security Checklist" /><published>2014-04-20T00:00:00+00:00</published><updated>2014-04-20T00:00:00+00:00</updated><id>https://blog.prashantrajan.com/2014/04/20/internet-startup-security-checklist</id><content type="html" xml:base="https://blog.prashantrajan.com/2014/04/20/internet-startup-security-checklist.html"><![CDATA[<p>I’ve been wanting to compile a checklist of basic precautions that could be useful to Internet startups (especially those in the SaaS space)
ever since the <a href="https://techcrunch.com/2013/10/29/hosting-service-mongohq-suffers-major-security-breach-that-explains-buffers-hack-over-the-weekend/">MongoHQ/Buffer security incident</a>.
As evident by that incident, a security breach at a SaaS provider could have catastrophic ripple effects - your customer’s customers data could be compromised!</p>

<p>TL;DR: The list basically boils down to using strong unique passwords, enabling multi-factor authentication (MFA) at third party providers
and moving as many things as possible behind a virtual private network (VPN).</p>

<h2 id="aws">AWS</h2>

<p>Run all your infrastructure (EC2, RDS, ElastiCache etc) within <a href="http://aws.amazon.com/vpc">VPC</a>.</p>

<ul>
  <li>
    <p>Most N-tier web architectures should be provisioned entirely within VPC and should not be publicly addressable.</p>
  </li>
  <li>
    <p>Do not enable or use the root Access Key, instead use <a href="https://aws.amazon.com/iam">IAM</a> and follow the <a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/IAMBestPractices.html">best practices</a>.</p>
  </li>
  <li>
    <p>Enable <a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_ManagingMFA.html">multi-factor authentication</a> on the root and all IAM accounts with AWS console access.</p>
  </li>
</ul>

<h2 id="google-apps">Google Apps</h2>

<p>A breach of an administrator account could allow an attacker to reset passwords and wreak havoc.</p>

<ul>
  <li>
    <p>Enable <a href="https://support.google.com/a/answer/184711">2-Step verification</a> for administrator accounts.</p>
  </li>
  <li>
    <p>Monitor and enforce <a href="https://support.google.com/a/answer/139399">strong user passwords</a>.</p>
  </li>
  <li>
    <p>Be diligent in suspending or deleting unused accounts.</p>
  </li>
</ul>

<h2 id="github">GitHub</h2>

<p>Access to your codebase could allow an attacker to gain insights on how to target attacks on your application.</p>

<ul>
  <li>
    <p>Ask every team member to enable <a href="https://help.github.com/articles/about-two-factor-authentication">2FA</a>.</p>
  </li>
  <li>
    <p>Audit user access; only assign users to repositories they need access to.</p>
  </li>
</ul>

<h2 id="domain-registrar">Domain Registrar</h2>

<p>A breach at your domain registrar account could allow an attacker to hijack your domain name or DNS.</p>

<ul>
  <li>
    <p>Use strong passwords and enable MFA.</p>
  </li>
  <li>
    <p>Use a <a href="http://en.wikipedia.org/wiki/Registrar-Lock">registrar lock</a>.</p>
  </li>
</ul>

<h2 id="internal-tools">Internal Tools</h2>

<p>This includes all employee/customer support tools, continuous integration, staging/demo environments etc.
All of these should be placed behind a VPN. <a href="http://openvpn.net">OpenVPN</a> is fairly easy to setup.</p>

<ul>
  <li>
    <p>Use <a href="http://en.wikipedia.org/wiki/Role-based_access_control">RBAC</a> for admin/backoffice tools. Only give people the permissions they need to perform their job functions.</p>
  </li>
  <li>
    <p>Maintain an audit/activity log of actions taken by admin accounts.</p>
  </li>
  <li>
    <p>Enforce strong password requirements for these applications and implement multi-factor authentication where feasible.</p>
  </li>
  <li>
    <p>Employees that need VPN access should get their own unique set of credentials.</p>
  </li>
</ul>

<h2 id="your-application">Your Application</h2>

<p>Learn some basic <a href="http://guides.rubyonrails.org/security.html">web security</a>.</p>

<ul>
  <li>
    <p>Don’t put in version control anything that’s meant to be secret: passwords, API keys etc.</p>
  </li>
  <li>
    <p>Use <a href="http://codahale.com/how-to-safely-store-a-password">bcrypt</a> to hash user passwords.</p>
  </li>
  <li>
    <p>Encrypt any user supplied OAuth tokens, API keys etc.</p>
  </li>
  <li>
    <p>Audit your external code dependencies; be mindful of the fact a lot of RubyGems, NPM packages etc aren’t written with YOUR security needs in mind.</p>
  </li>
</ul>

<h2 id="third-party-application-services">Third Party Application Services</h2>

<p>Make sure sensitive information is being scrubbed before being sent from your application to third party tools such as the following:</p>

<ul>
  <li>
    <p>Application monitoring services - New Relic, Librato, Datadog etc.</p>
  </li>
  <li>
    <p>Application logging services - Loggly, Papertrail, Logentries etc.</p>
  </li>
  <li>
    <p>Error monitoring services - Airbrake, Bugsnag, Honeybadger etc.</p>
  </li>
</ul>

<p><br />
As your startup grows, so too will <a href="http://firstround.com/article/Evernotes-CTO-on-Your-Biggest-Security-Worries-From-Three-Employees-to-300">your security requirements</a>.</p>]]></content><author><name>Prashant Nadarajan</name></author><summary type="html"><![CDATA[I’ve been wanting to compile a checklist of basic precautions that could be useful to Internet startups (especially those in the SaaS space) ever since the MongoHQ/Buffer security incident. As evident by that incident, a security breach at a SaaS provider could have catastrophic ripple effects - your customer’s customers data could be compromised!]]></summary></entry><entry><title type="html">Redis Automatic Failover with Sentinel</title><link href="https://blog.prashantrajan.com/2013/09/15/redis-automatic-failover-with-sentinel.html" rel="alternate" type="text/html" title="Redis Automatic Failover with Sentinel" /><published>2013-09-15T00:00:00+00:00</published><updated>2013-09-15T00:00:00+00:00</updated><id>https://blog.prashantrajan.com/2013/09/15/redis-automatic-failover-with-sentinel</id><content type="html" xml:base="https://blog.prashantrajan.com/2013/09/15/redis-automatic-failover-with-sentinel.html"><![CDATA[<p><a href="http://redis.io">Redis</a> is a popular key-value store that is fast, reliable and simple to administer.
While Redis asynchronous <a href="http://redis.io/topics/replication">master-slave replication</a> is well known,
<a href="http://redis.io/topics/sentinel">Sentinel</a> seems to be a little known feature.
Sentinel is a built-in feature in v2.6+ that provides an automatic failover mechanism,
i.e. promotion of a slave to replace a failing master, reconfiguring other slaves to use a new master and
informing client applications about the new address to use when connecting to a master.</p>

<h2 id="limitations">Limitations</h2>

<p>The official docs note that Sentinel is a work in progress.
It’s important to understand Sentinel’s operating parameters and its limitations as a high availability (HA)
solution for your particular environment. For that, I defer you to these excellent posts:</p>

<ul>
  <li><a href="http://aphyr.com/posts/283-call-me-maybe-redis">Call me maybe: Redis</a></li>
  <li><a href="http://antirez.com/news/55">Reply to Aphyr attack to Sentinel</a></li>
  <li><a href="http://aphyr.com/posts/287-asynchronous-replication-with-failover">Asynchronous replication with failover</a></li>
  <li><a href="http://antirez.com/news/56">Reply to Aphyr, part 2</a></li>
</ul>

<p><br /></p>
<h2 id="setup">Setup</h2>

<p>A prerequisite for using Sentinel for automatic failover is to have a master-slave replication setup.
Both replication and Sentinel are easy to setup and well documented in the official docs.</p>

<p>Once you have everything setup, you then need to configure your Redis clients (web apps, queues etc) to connect
to the Sentinel instances instead of directly to a master. The Sentinel instances will then report back to the clients
on the appropriate address to use to connect to an available master.</p>

<h2 id="client-library-support">Client Library Support</h2>

<p>Redis Sentinel support in the various <a href="http://redis.io/clients">clients</a> libraries seems limited at this time.
Some client libraries have extensions that provide Sentinel support.
You could implement Sentinel support in your desired client library by following
the <a href="http://redis.io/topics/sentinel-clients">Sentinel Client Guidelines</a>.</p>

<p>Listed below are examples for configuring clients when using Ruby &amp; Node.js.
The examples assume a YAML config similar to this:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">redis</span><span class="pi">:</span>
  <span class="na">master_name</span><span class="pi">:</span> <span class="s">master-name</span>
  <span class="na">sentinels</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">sentinel1.example.com</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">26380</span>
    <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">sentinel1.example.com</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">26381</span>
    <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">sentinel2.example.com</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">26380</span>
    <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">sentinel2.example.com</span>
      <span class="na">port</span><span class="pi">:</span> <span class="s">26381</span></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">master_name</code> should match the name you use in the <a href="https://github.com/antirez/redis/blob/2.6/sentinel.conf">sentinel.conf</a>
and the <code class="language-plaintext highlighter-rouge">host</code> &amp; <code class="language-plaintext highlighter-rouge">port</code> entries should match the hosts and ports that you run Sentinels on.</p>

<h3 id="ruby">Ruby</h3>

<p>At the time of writing, the <a href="https://github.com/redis/redis-rb">redis-rb</a> client doesn’t yet support Sentinel.
<a href="https://github.com/flyerhzm/redis-sentinel">redis-sentinel</a> extends redis-rb to provide the necessary support
and acts as a drop-in replacement anywhere a redis-rb client is expected.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># Ruby Examples</span>

<span class="c1"># Redis.new works as expected with the added benefit of clients being informed</span>
<span class="c1"># when a new master is elected</span>
<span class="n">redis_config</span> <span class="o">=</span> <span class="no">YAML</span><span class="p">.</span><span class="nf">load_file</span><span class="p">(</span><span class="s1">'/path/to/redis_config.yml'</span><span class="p">)[</span><span class="s1">'redis'</span><span class="p">]</span>
<span class="n">redis</span> <span class="o">=</span> <span class="no">Redis</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">redis_config</span><span class="p">)</span>
<span class="n">redis</span><span class="p">.</span><span class="nf">info</span>

<span class="c1"># Configure Sidekiq in Ruby on Rails (config/initializers/sidekiq.rb)</span>
<span class="nb">require</span> <span class="s1">'redis/namespace'</span>

<span class="n">redis_config</span> <span class="o">=</span> <span class="no">YAML</span><span class="p">.</span><span class="nf">load_file</span><span class="p">(</span><span class="s1">'/path/to/redis_config.yml'</span><span class="p">)[</span><span class="s1">'redis'</span><span class="p">]</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">options</span><span class="p">[</span><span class="ss">:size</span><span class="p">]</span> <span class="o">||</span> <span class="p">(</span><span class="no">Sidekiq</span><span class="p">.</span><span class="nf">server?</span> <span class="p">?</span> <span class="p">(</span><span class="no">Sidekiq</span><span class="p">.</span><span class="nf">options</span><span class="p">[</span><span class="ss">:concurrency</span><span class="p">]</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> <span class="p">:</span> <span class="mi">5</span><span class="p">)</span>
<span class="n">namespace</span> <span class="o">=</span> <span class="s1">'sidekiq'</span>
<span class="n">timeout</span> <span class="o">=</span> <span class="mi">1</span>

<span class="no">Sidekiq</span><span class="p">.</span><span class="nf">configure_server</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">redis</span> <span class="o">=</span> <span class="no">ConnectionPool</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:timeout</span> <span class="o">=&gt;</span> <span class="n">timeout</span><span class="p">,</span> <span class="ss">:size</span> <span class="o">=&gt;</span> <span class="n">size</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">Redis</span><span class="o">::</span><span class="no">Namespace</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">namespace</span><span class="p">,</span> <span class="ss">:redis</span> <span class="o">=&gt;</span> <span class="no">Redis</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">redis_config</span><span class="p">))</span>
  <span class="k">end</span>

  <span class="n">config</span><span class="p">.</span><span class="nf">redis</span> <span class="o">=</span> <span class="n">redis</span>
<span class="k">end</span>

<span class="no">Sidekiq</span><span class="p">.</span><span class="nf">configure_client</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">redis</span> <span class="o">=</span> <span class="no">ConnectionPool</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:timeout</span> <span class="o">=&gt;</span> <span class="n">timeout</span><span class="p">,</span> <span class="ss">:size</span> <span class="o">=&gt;</span> <span class="n">size</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">Redis</span><span class="o">::</span><span class="no">Namespace</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">namespace</span><span class="p">,</span> <span class="ss">:redis</span> <span class="o">=&gt;</span> <span class="no">Redis</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">redis_config</span><span class="p">))</span>
  <span class="k">end</span>

  <span class="n">config</span><span class="p">.</span><span class="nf">redis</span> <span class="o">=</span> <span class="n">redis</span>
<span class="k">end</span></code></pre></figure>

<p><br /></p>
<h3 id="nodejs">Node.js</h3>

<p>At the time of writing, the <a href="https://github.com/mranney/node_redis">node_redis</a> client doesn’t yet support Sentinel.
<a href="https://github.com/ortoo/node-redis-sentinel">node-redis-sentinel</a> extends node_redis to provide the necessary support
and acts as a drop-in replacement anywhere a node_redis client is expected.</p>

<figure class="highlight"><pre><code class="language-coffeescript" data-lang="coffeescript"><span class="c1"># CoffeeScript Examples</span>

<span class="nx">yaml</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s">'js-yaml'</span><span class="p">)</span> <span class="c1"># https://github.com/nodeca/js-yaml</span>
<span class="nx">redis</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s">'redis'</span><span class="p">)</span> <span class="c1"># https://github.com/mranney/node_redis</span>
<span class="nx">sentinel</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s">'redis-sentinel'</span><span class="p">)</span> <span class="c1"># https://github.com/ortoo/node-redis-sentinel</span>

<span class="nx">redis_config</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s">'/path/to/redis_config.yml'</span><span class="p">)</span>
<span class="nx">masterName</span> <span class="o">=</span> <span class="nx">redis_config</span><span class="p">.</span><span class="na">master_name</span>
<span class="nx">sentinels</span> <span class="o">=</span> <span class="nx">redis_config</span><span class="p">.</span><span class="na">sentinels</span>
<span class="nx">opts</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># standard node_redis client options</span>

<span class="nx">redisClient</span> <span class="o">=</span> <span class="nx">sentinel</span><span class="p">.</span><span class="na">createClient</span><span class="p">(</span><span class="nx">sentinels</span><span class="p">,</span> <span class="nx">masterName</span><span class="p">,</span> <span class="nx">opts</span><span class="p">)</span></code></pre></figure>]]></content><author><name>Prashant Nadarajan</name></author><summary type="html"><![CDATA[Redis is a popular key-value store that is fast, reliable and simple to administer. While Redis asynchronous master-slave replication is well known, Sentinel seems to be a little known feature. Sentinel is a built-in feature in v2.6+ that provides an automatic failover mechanism, i.e. promotion of a slave to replace a failing master, reconfiguring other slaves to use a new master and informing client applications about the new address to use when connecting to a master.]]></summary></entry><entry><title type="html">SOA Ruby Development with Foreman</title><link href="https://blog.prashantrajan.com/2013/06/20/soa-ruby-development-with-foreman.html" rel="alternate" type="text/html" title="SOA Ruby Development with Foreman" /><published>2013-06-20T00:00:00+00:00</published><updated>2013-06-20T00:00:00+00:00</updated><id>https://blog.prashantrajan.com/2013/06/20/soa-ruby-development-with-foreman</id><content type="html" xml:base="https://blog.prashantrajan.com/2013/06/20/soa-ruby-development-with-foreman.html"><![CDATA[<p><a href="https://github.com/ddollar/foreman">Foreman</a> is an intra-app process manager used in a development environment.
Although its main use case is that, I’ve started to use it to manage inter-app dependencies as well.</p>

<p><a href="https://github.com/basecamp/pow">Pow</a> is a simple Rack server that also contains a DNS server capable of port proxying.</p>

<p>These two tools <a href="http://robots.thoughtbot.com/post/40110176152/foreman-as-process-manager-pow-as-dns-server-and-http">combined</a>
allow for easy management of a <a href="http://en.wikipedia.org/wiki/Service-oriented_architecture">SOA</a> based local development environment.</p>

<p>Imagine a primary Ruby on Rails application that depends on a couple of other services
(Rails, Rack, Sinatra, Faye, Node.js etc) to be fully functional in development.
These other services/apps could have their own Gemfile and process
dependencies (worker, clock etc) that are also managed locally by Foreman.</p>

<p><a href="http://pivotallabs.com/service-oriented-foreman">Service Oriented Foreman</a> by Matthew Kocher
describes an approach that uses Foreman to manage these inter-app processes. Here’s a more complete example of that approach:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># primary_app/Procfile</span>
<span class="ss">web: </span><span class="n">bundle</span> <span class="nb">exec</span> <span class="n">unicorn</span> <span class="o">-</span><span class="nb">p</span> <span class="vg">$PORT</span> <span class="o">-</span><span class="n">c</span> <span class="p">.</span><span class="nf">/</span><span class="n">config</span><span class="o">/</span><span class="n">unicorn</span><span class="p">.</span><span class="nf">rb</span>
<span class="ss">worker: </span><span class="n">bundle</span> <span class="nb">exec</span> <span class="n">sidekiq</span> <span class="o">-</span><span class="no">C</span> <span class="n">config</span><span class="o">/</span><span class="n">sidekiq</span><span class="p">.</span><span class="nf">yml</span>
<span class="ss">service1: </span><span class="n">script</span><span class="o">/</span><span class="n">run_app</span> <span class="n">service1_app</span></code></pre></figure>

<p><br /></p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># primary_app/.foreman</span>
<span class="ss">port: </span><span class="mi">8000</span></code></pre></figure>

<p><br /></p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#!/usr/bin/env ruby</span>

<span class="c1"># primary_app/script/run_app</span>
<span class="nb">require</span> <span class="s1">'bundler'</span>

<span class="n">app_dir</span> <span class="o">=</span> <span class="s2">"../</span><span class="si">#{</span><span class="no">ARGV</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span>
<span class="n">cmd</span> <span class="o">=</span> <span class="sx">%Q{bash -lc "cd </span><span class="si">#{</span><span class="n">app_dir</span><span class="si">}</span><span class="sx"> &amp;&amp; bundle exec foreman start"}</span>
<span class="no">Bundler</span><span class="p">.</span><span class="nf">with_clean_env</span> <span class="k">do</span>
  <span class="nb">system</span><span class="p">({</span><span class="s1">'NOEXEC'</span> <span class="o">=&gt;</span> <span class="s1">'skip'</span><span class="p">},</span> <span class="n">cmd</span><span class="p">)</span>
<span class="k">end</span></code></pre></figure>

<p><br /></p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># service1_app/Procfile</span>
<span class="ss">web: </span><span class="n">bundle</span> <span class="nb">exec</span> <span class="n">unicorn</span> <span class="o">-</span><span class="nb">p</span> <span class="vg">$PORT</span> <span class="o">-</span><span class="n">c</span> <span class="p">.</span><span class="nf">/</span><span class="n">config</span><span class="o">/</span><span class="n">unicorn</span><span class="p">.</span><span class="nf">rb</span></code></pre></figure>

<p><br /></p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># service1_app/.foreman</span>
<span class="ss">port: </span><span class="mi">8001</span></code></pre></figure>

<p>Assuming the <code class="language-plaintext highlighter-rouge">Service1 App</code> has the relevant <a href="http://ddollar.github.io/foreman/#DEFAULT-OPTIONS">.foreman</a>
file then typing <code class="language-plaintext highlighter-rouge">foreman start</code> in the <code class="language-plaintext highlighter-rouge">Primary App</code> terminal
will also start all the associated services specified in the primary <a href="http://ddollar.github.io/foreman/#PROCFILE">Procfile</a>.</p>

<p>In order to easily keep the associated services up-to-date (which may be developed by other teams),
it’s nifty to include a helper script in the Primary App that helps make this easy. Here’s an example of what that may look like:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#!/usr/bin/env ruby</span>

<span class="c1"># primary_app/script/update_apps</span>
<span class="nb">require</span> <span class="s1">'yaml'</span>

<span class="p">{</span> <span class="s1">'service1_app'</span>      <span class="o">=&gt;</span> <span class="p">{},</span>
  <span class="s1">'service2_app'</span>      <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:bundle</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:pow</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">},</span>
  <span class="s1">'service3_app'</span>      <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:bundle</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:pow</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:npm</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">}</span>
<span class="p">}.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">app</span><span class="p">,</span> <span class="n">opts</span><span class="o">|</span>

  <span class="n">app_dir</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"../</span><span class="si">#{</span><span class="n">app</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
  <span class="n">bundle</span> <span class="o">=</span> <span class="n">opts</span><span class="p">[</span><span class="ss">:bundle</span><span class="p">]</span>
  <span class="n">npm</span> <span class="o">=</span> <span class="n">opts</span><span class="p">[</span><span class="ss">:npm</span><span class="p">]</span>
  <span class="n">pow</span> <span class="o">=</span> <span class="n">opts</span><span class="p">[</span><span class="ss">:pow</span><span class="p">]</span>

  <span class="n">bundle_install</span> <span class="o">=</span> <span class="n">bundle</span> <span class="p">?</span> <span class="s1">'&amp;&amp; bundle install'</span> <span class="p">:</span> <span class="s1">''</span>
  <span class="n">npm_install</span> <span class="o">=</span> <span class="n">npm</span> <span class="p">?</span> <span class="s1">'&amp;&amp; npm install'</span> <span class="p">:</span> <span class="s1">''</span>

  <span class="nb">puts</span> <span class="s2">"=== Updating App: </span><span class="si">#{</span><span class="n">app</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">if</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">app_dir</span><span class="p">)</span>
    <span class="n">update_apps_cmd</span> <span class="o">=</span>
      <span class="sx">%Q{bash -lc "cd </span><span class="si">#{</span><span class="n">app_dir</span><span class="si">}</span><span class="sx"> &amp;&amp; git pull --rebase </span><span class="si">#{</span><span class="n">bundle_install</span><span class="si">}</span><span class="sx"> </span><span class="si">#{</span><span class="n">npm_install</span><span class="si">}</span><span class="sx">"}</span>
  <span class="k">else</span>
    <span class="n">update_apps_cmd</span> <span class="o">=</span> <span class="sx">%Q{bash -lc "cd </span><span class="si">#{</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s1">'../'</span><span class="p">)</span><span class="si">}</span><span class="sx"> &amp;&amp;
      git clone git@github.com:myorg/</span><span class="si">#{</span><span class="n">app</span><span class="si">}</span><span class="sx">.git &amp;&amp;
      cd </span><span class="si">#{</span><span class="n">app_dir</span><span class="si">}</span><span class="sx"> </span><span class="si">#{</span><span class="n">bundle_install</span><span class="si">}</span><span class="sx"> </span><span class="si">#{</span><span class="n">npm_install</span><span class="si">}</span><span class="sx">"}</span>
  <span class="k">end</span>
  <span class="nb">system</span><span class="p">(</span><span class="n">update_apps_cmd</span><span class="p">)</span>

  <span class="k">if</span> <span class="no">File</span><span class="p">.</span><span class="nf">exists?</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">app_dir</span><span class="si">}</span><span class="s2">/script/copy_configs"</span><span class="p">)</span>
    <span class="nb">puts</span> <span class="s2">"=== Copying example configs for </span><span class="si">#{</span><span class="n">app</span><span class="si">}</span><span class="s2">"</span>
    <span class="nb">system</span><span class="p">(</span><span class="sx">%Q{bash -lc "cd </span><span class="si">#{</span><span class="n">app_dir</span><span class="si">}</span><span class="sx"> &amp;&amp; script/copy_configs"}</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">pow</span>
    <span class="n">pow_app_config</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"~/.pow/</span><span class="si">#{</span><span class="n">app</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="k">unless</span> <span class="no">File</span><span class="p">.</span><span class="nf">exists?</span><span class="p">(</span><span class="n">pow_app_config</span><span class="p">)</span>
      <span class="nb">puts</span> <span class="s2">"=== Creating Pow port proxy config for </span><span class="si">#{</span><span class="n">app</span><span class="si">}</span><span class="s2">"</span>

      <span class="n">port</span> <span class="o">=</span> <span class="no">YAML</span><span class="o">::</span><span class="n">load_file</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">app_dir</span><span class="si">}</span><span class="s2">/.foreman"</span><span class="p">)[</span><span class="s1">'port'</span><span class="p">]</span>
      <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">pow_app_config</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">port</span><span class="si">}</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>

<span class="k">end</span></code></pre></figure>

<p>This setup has been working well enough with just a few services in development and using <a href="https://github.com/bblimke/webmock">Webmock</a>
to stub out requests in the test environment. Going forward, each service could also provide a private library/gem that
mocks its interface out in the test environment.</p>

<p>There are other approaches to managing services in development that are worth
evaluating like <a href="https://brandur.org/service-stubs">service stubs</a> and more
comprehensively using Vagrant as alluded to in this <a href="http://www.youtube.com/watch?feature=player_detailpage&amp;v=o5u87SZ6S9M#t=1296s">talk</a>.</p>]]></content><author><name>Prashant Nadarajan</name></author><summary type="html"><![CDATA[Foreman is an intra-app process manager used in a development environment. Although its main use case is that, I’ve started to use it to manage inter-app dependencies as well.]]></summary></entry><entry><title type="html">Leveling Up on Chef Best Practices</title><link href="https://blog.prashantrajan.com/2013/06/14/leveling-up-chef-best-practices.html" rel="alternate" type="text/html" title="Leveling Up on Chef Best Practices" /><published>2013-06-14T00:00:00+00:00</published><updated>2013-06-14T00:00:00+00:00</updated><id>https://blog.prashantrajan.com/2013/06/14/leveling-up-chef-best-practices</id><content type="html" xml:base="https://blog.prashantrajan.com/2013/06/14/leveling-up-chef-best-practices.html"><![CDATA[<p>I first started working with <a href="http://www.opscode.com/chef/">Opscode Chef</a> in early 2011 while on a consulting project
(native environments’ support was still a beta feature back then).
Since late 2012, I’ve been working extensively with Chef once again for <a href="https://www.bitium.com">Bitium</a>.</p>

<p>I’ve been looking to level up my Chef skills, specifically on the latest best practices.
Opscode seems to have an aversion to promulgating current standards and leaving it up to the community to come up
with whatever works best for them.</p>

<p>These blog posts and presentations shed light on some good current practices:</p>

<ul>
  <li>
    <p><a href="http://www.opscode.com/blog/chefconf-talks/beginning-chef-antipatterns-julian-dunn">Beginning Chef Antipatterns</a>
(video &amp; slides)</p>
  </li>
  <li>
    <p><a href="http://www.opscode.com/blog/chefconf-talks/the-berkshelf-way-jamie-winsor">The Berkshelf Way</a> (video &amp; slides)</p>
  </li>
  <li>
    <p><a href="http://devopsanywhere.blogspot.com/2012/11/how-to-write-reusable-chef-cookbooks.html">How to Write Reusable Chef Cookbooks, Gangnam Style</a></p>
  </li>
  <li>
    <p><a href="http://dougireton.com/blog/2013/02/16/chef-cookbook-anti-patterns">Chef: Patterns and Anti-Patterns for Cookbooks, Environments, Roles</a></p>
  </li>
  <li>
    <p><a href="http://www.opscode.com/blog/chefconf-talks/cookbook-refactoring-abstracting-logic-to-rubygems-seth-vargo">Cookbook Refactoring &amp; Abstracting Logic to RubyGems</a> (video &amp; slides)</p>
  </li>
  <li>
    <p><a href="http://www.opscode.com/blog/chefconf-talks/getting-more-chefs-in-the-kitchen-developing-infrastructure-as-a-team-andrew-gross">Getting More Chefs in the Kitchen – Developing Infrastructure as a Team</a>
(video &amp; slides)</p>
  </li>
</ul>

<p><br />
Here’s a summary of some of the patterns, practices &amp; terminology that I have come across that weren’t prevalent back
when I first started working with Chef.</p>

<h3 id="single-repo-per-cookbook">Single Repo Per Cookbook</h3>

<p>A <code class="language-plaintext highlighter-rouge">single-chef-repo</code> with all the cookbooks used to be the norm. There are still benefits to this all in one repo approach -
everything is centralized in a single view and it’s easy to search across the cookbooks.</p>

<p>The <code class="language-plaintext highlighter-rouge">single-repo-per-cookbook</code> approach on the other hand makes it easier to keep up-to-date with upstream community cookbooks,
to open source cookbooks and to a certain extent it just makes more sense for cookbooks to be versioned and managed independently.</p>

<p>A common convention seems to be to have a separate GitHub Org account to host these individual cookbooks, e.g. <a href="https://github.com/opscode-cookbooks/">github.com/opscode-cookbooks</a>.
I’m more in favour of just sticking with a primary Github Org account and naming the repos in the form <code class="language-plaintext highlighter-rouge">&lt;name&gt;-cookbook</code>.</p>

<p><a href="http://docs.opscode.com/essentials_data_bags.html">Data bags</a>, <a href="http://docs.opscode.com/essentials_roles.html">roles</a>,
<a href="http://docs.opscode.com/essentials_environments.html">environments</a> and such should remain in a private chef-repo.</p>

<h3 id="cookbook-bundlers">Cookbook Bundlers</h3>

<p><a href="http://berkshelf.com">Berkshelf</a> and <a href="https://github.com/applicationsonline/librarian-chef">Librarian-Chef</a> are cookbook
bundlers (in the spirit of <a href="https://bundler.io/">Bundler</a>) but that are philosophically very different from each other
in how they work.</p>

<p>I think the rule of thumb when deciding on which to adopt depends on how you prefer to view/manage your cookbook infrastructure:</p>

<p>If you’re using the <code class="language-plaintext highlighter-rouge">single-repo-per-cookbook</code> approach then <code class="language-plaintext highlighter-rouge">Berkshelf</code> is likely a great fit.
Make sure to checkout the nifty built-in <a href="http://berkshelf.com/#vagrant_with_berkshelf">Vagrant integration</a>
that makes the local build test/inspect cycle really easy.</p>

<p>If you’re using the <code class="language-plaintext highlighter-rouge">single-chef-repo</code> approach then <code class="language-plaintext highlighter-rouge">Librarian-Chef</code> could improve your workflow, just move your
existing privately maintained cookbooks into a <code class="language-plaintext highlighter-rouge">site-cookbooks</code> directory within your chef-repo.
Berkshelf could still work with <a href="https://github.com/RiotGames/berkshelf/issues/535#issuecomment-18105296">this approach</a>
but it’s definitely <a href="https://github.com/RiotGames/berkshelf/issues/535#issuecomment-18123272">not the recommended way</a>.</p>

<h3 id="cookbook-types">Cookbook Types</h3>

<p>There are a bunch of terms being bandied around to describe the type/flavor of a cookbook.
Since there’s no official description for these terms, I thought I’d take a stab at trying to restate some of the descriptions
I’ve come across:</p>

<p><code class="language-plaintext highlighter-rouge">Application Cookbook</code> - just what you would imagine it to be, something like a <code class="language-plaintext highlighter-rouge">mycompany-app</code> cookbook that installs and
configures a complete application (e.g. a web app). It could depend on all other types of cookbooks.
<a href="https://github.com/opscode-cookbooks/postgresql">Postgresql</a> &amp; <a href="https://github.com/opscode-cookbooks/nginx">Nginx</a>
should be considered Application cookbooks too! These cookbooks should have their versions locked at the Environment level.</p>

<p><code class="language-plaintext highlighter-rouge">Library Cookbook</code> - contains <a href="http://docs.opscode.com/essentials_cookbook_definitions.html">Definitions</a>,
<a href="http://docs.opscode.com/lwrp.html">LWRPs</a>, <a href="http://docs.opscode.com/essentials_cookbook_libraries.html">Libraries</a> that are
used by other cookbooks. They may or may not include recipes.
The <a href="https://github.com/opscode-cookbooks/database">database</a> cookbook is a good example of a Library cookbook.
These cookbooks shouldn’t be directly assigned to nodes. Cookbooks that depend on Library cookbooks should
lock their required Library versions in their <a href="http://docs.opscode.com/essentials_cookbook_metadata.html">metadata.rb</a>.</p>

<p><code class="language-plaintext highlighter-rouge">Wrapper Cookbook</code> - a more specific type of Application cookbook that depends directly on a single other Application cookbook and potentially
Library cookbooks. For example, a hypothetical <code class="language-plaintext highlighter-rouge">bitium-phantomjs</code> cookbook could wrap around the
<a href="http://community.opscode.com/cookbooks/phantomjs">phantomjs</a> community cookbook and contain attribute overrides and
recipes that orchestrates phantomjs to our company’s specific needs.</p>

<h3 id="roles-cookbook">Roles Cookbook</h3>

<p>I’ve been using roles extensively to maintain run lists.
Roles aren’t versioned unlike cookbooks so there’s always the chance a bad change could end up in a production
environment by accident.</p>

<p>I’m planning on switching to using a <code class="language-plaintext highlighter-rouge">roles cookbook</code> that contains recipes like <code class="language-plaintext highlighter-rouge">base.rb</code> &amp; <code class="language-plaintext highlighter-rouge">web_server.rb</code>
that use <code class="language-plaintext highlighter-rouge">include_recipe</code> to define a run list. I’ll still use the native roles but they would just have
a call to the corresponding role recipe within its run list. Here’s an example:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># roles-cookbook/metadata.rb</span>
<span class="nb">name</span>    <span class="s1">'roles'</span>
<span class="n">version</span> <span class="s1">'0.1.0'</span>

<span class="n">recipe</span> <span class="s1">'base'</span><span class="p">,</span> <span class="s1">'Base Server Role'</span>
<span class="n">recipe</span> <span class="s1">'web_server'</span><span class="p">,</span> <span class="s1">'Web Server Role'</span>

<span class="n">depends</span> <span class="s1">'hostname'</span>
<span class="n">depends</span> <span class="s1">'nginx'</span>
<span class="n">depends</span> <span class="s1">'ntp'</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># roles-cookbook/recipes/base.rb</span>
<span class="n">include_recipe</span> <span class="s1">'hostname::default'</span>
<span class="n">include_recipe</span> <span class="s1">'ntp::default'</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># roles-cookbook/recipes/web_server.rb</span>
<span class="n">include_recipe</span> <span class="s1">'roles::base'</span>
<span class="n">include_recipe</span> <span class="s1">'nginx::default'</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># roles/base.rb</span>
<span class="nb">name</span> <span class="s1">'base'</span>
<span class="n">description</span> <span class="s1">'Base role for all servers'</span>

<span class="n">run_list</span><span class="p">(</span>
  <span class="s1">'recipe[roles::base]'</span>
<span class="p">)</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># roles/web-server.rb</span>
<span class="nb">name</span> <span class="s1">'web-server'</span>
<span class="n">description</span> <span class="s1">'Web server role'</span>

<span class="n">run_list</span><span class="p">(</span>
  <span class="s1">'recipe[roles::web_server]'</span>
<span class="p">)</span></code></pre></figure>

<p>Remember to bump up the version of the <code class="language-plaintext highlighter-rouge">roles cookbook</code> when making changes and <code class="language-plaintext highlighter-rouge">freeze</code> it when uploading to the Chef Server.</p>

<p>The <code class="language-plaintext highlighter-rouge">roles cookbook</code> can now be versioned in an environment file:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># environments/production.rb</span>
<span class="nb">name</span> <span class="s1">'production'</span>
<span class="n">description</span> <span class="s1">'Production Environment'</span>

<span class="n">cookbook_versions</span><span class="p">({</span>
  <span class="s1">'roles'</span> <span class="o">=&gt;</span> <span class="s1">'= 0.1.0'</span>
  <span class="c1"># ... and other cookbooks</span>
<span class="p">})</span></code></pre></figure>

<p>You could probably do away completely with the native roles and just use the <code class="language-plaintext highlighter-rouge">roles' recipes</code> directly in a node’s run list.
I’m going to keep them for now but using them only as described in the examples above.</p>

<h3 id="private-recipes">Private Recipes</h3>

<p>This is just a simple convention to use an underscore to prefix a recipe name to mark it as private, e.g. <code class="language-plaintext highlighter-rouge">recipes/_common_setup.rb</code>.
Some guidelines for when to make a recipe private:</p>

<ul>
  <li>
    <p>meant for logical code separation</p>
  </li>
  <li>
    <p>included only in recipes from the <em>same</em> cookbook</p>
  </li>
  <li>
    <p>not to be included directly in the run list of a node</p>
  </li>
</ul>

<h3 id="cookbook-testing">Cookbook Testing</h3>

<p>The testing landscape for Chef has definitely gone through a revolution.
There’s now a good selection of tools for every category of tests.</p>

<p><code class="language-plaintext highlighter-rouge">Syntax Checking</code></p>

<ul>
  <li>Built-in <a href="http://docs.opscode.com/knife_cookbook.html#test">knife cookbook test</a>.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">Linting</code></p>

<ul>
  <li><a href="http://acrmp.github.io/foodcritic/">Foodcritic</a>.
See also the additional rules by <a href="https://github.com/etsy/foodcritic-rules">Etsy</a> and
<a href="https://github.com/customink-webops/foodcritic-rules">CustomInk</a>.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">Unit Tests</code></p>

<ul>
  <li><a href="https://github.com/acrmp/chefspec">ChefSpec</a>.
Built on top of <a href="https://www.relishapp.com/rspec">RSpec</a>.
It performs assertions without actually converging a node so it should be fast.
See also <a href="https://github.com/customink/fauxhai">Fauxhai</a> for mocking out Ohai attributes in specs.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">Integration Tests</code></p>

<ul>
  <li>
    <p><a href="https://github.com/calavera/minitest-chef-handler">Minitest Chef Handler</a>.
Built on top of <a href="https://github.com/seattlerb/minitest">minitest</a> and works with Vagrant.
It does its assertions post convergence, i.e after a Vagrant up/provision run.</p>
  </li>
  <li>
    <p><a href="https://github.com/opscode/test-kitchen">Test Kitchen</a>. Cross platform testing with Vagrant.
The 1.0 version (a complete rewrite) is still alpha level and lacking <a href="http://docs.kitchen-ci.org">documentation</a>.
It uses Vagrant to run Minitest Chef Handler across various platforms, similar to how <a href="https://travis-ci.org">Travis CI</a> runs tests across
different language runtimes.
See this <a href="http://www.opscode.com/blog/chefconf-talks/test-kitchen-multi-platform-integration-testing-for-the-masses-fletcher-nichol-seth-chisamore-and-joshua-timberman">talk</a>
announcing the 1.0 version.</p>
  </li>
  <li>
    <p><a href="http://www.vagrantup.com/">Vagrant</a>.
Plain old manual testing using the <a href="http://docs.vagrantup.com/v2/provisioning/chef_solo.html">Chef Solo Provisioner</a>.</p>
  </li>
</ul>

<p>You can use <a href="https://github.com/customink/strainer">Strainer</a> to manage your various tests for a cookbook, similar to how
<a href="https://github.com/ddollar/foreman">Foreman</a> manages application processes.</p>

<p>See <a href="https://speakerdeck.com/nathenharvey/testing-your-automation">here</a> &amp; <a href="http://www.iflowfor8hours.info/2012/11/chef-testing-stratagies-compared">here</a>
for more in-depth coverage on testing.</p>

<h3 id="cookbook-build-system--pipeline">Cookbook Build System / Pipeline</h3>

<p>This just means having an automated system for <em>building</em> (testing, versioning &amp; deploying) your cookbooks.</p>

<p>You could use a CI system like <a href="http://jenkins-ci.org">Jenkins</a> with a flow similar to the following:</p>

<ul>
  <li>
    <p>Make changes to a cookbook locally.</p>
  </li>
  <li>
    <p>Push changes to a remote repo.</p>
  </li>
  <li>
    <p>A <code class="language-plaintext highlighter-rouge">Jenkins Job</code> runs the cookbook’s tests (possibly using <code class="language-plaintext highlighter-rouge">Strainer</code>).</p>
  </li>
  <li>
    <p>If all the tests pass, use <a href="https://github.com/jonlives/knife-spork">Knife Spork</a>
to bump up the cookbook version, upload &amp; freeze the cookbook on the Chef Server and potentially promote the cookbook into another
environment by updating the environment’s cookbook version constraint.</p>
  </li>
  <li>
    <p>Potentially invoke a <code class="language-plaintext highlighter-rouge">chef-client</code> run on the relevant nodes in a specific environment.</p>
  </li>
</ul>

<p>Special mention here for <a href="https://github.com/RiotGames/ridley">Ridley</a>, a well written Ruby based Chef API client.
It’s a useful tool for building out a custom workflow or supporting tools outside of <a href="http://docs.opscode.com/knife.html">knife</a>.
I’ve been using it in <a href="http://capistranorb.com">Capistrano</a> recipes to help with application deployments
and will likely use it in the build system.</p>

<p><em>Update (June 19):</em> Seth Vargo’s <a href="https://speakerdeck.com/sethvargo/test-driven-infrastructure-with-chef">Test Driven Infrastructure with Chef</a>
presentation is a good how-to for getting started with a simple build system.</p>]]></content><author><name>Prashant Nadarajan</name></author><summary type="html"><![CDATA[I first started working with Opscode Chef in early 2011 while on a consulting project (native environments’ support was still a beta feature back then). Since late 2012, I’ve been working extensively with Chef once again for Bitium.]]></summary></entry></feed>