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

  <title><![CDATA[Shit Gary Says]]></title>
  <link href="http://garylarizza.com/atom.xml" rel="self"/>
  <link href="http://garylarizza.com/"/>
  <updated>2017-08-25T16:13:19-05:00</updated>
  <id>http://garylarizza.com/</id>
  <author>
    <name><![CDATA[Gary larizza]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Some Other Beginning's End]]></title>
    <link href="http://garylarizza.com/blog/2017/08/25/some-other-beginnings-end/"/>
    <updated>2017-08-25T15:35:29-05:00</updated>
    <id>http://garylarizza.com/blog/2017/08/25/some-other-beginnings-end</id>
    <content type="html"><![CDATA[<p>My first day at Puppet, James Turnbull sat me down next to Jeff McCune and
waited for things to happen. That was partly because he knew that Jeff&rsquo;s
attention to detail would offset my attention to <code>git commit -a -m 'blkajdfs'</code>,
and partly because there were about 30 of us in the office and we all had shit
to do. Jeff was kinda working on an MCollective module back then, and I had
been using it pretty heavily at the school, so I decided to jump right in and
start hacking it up. To make a long story short, that experience working on the
MCollective module resulted in:</p>

<ol>
<li><a href="https://projects.puppetlabs.com/issues/8040">Ticket number 8040 on class containment being filed by Jeff</a> (AKA &ldquo;How the Anchor pattern was born&rdquo;)</li>
<li>An understanding in how to use git as something more than just glorified rsync</li>
<li>An &ldquo;Odd Couple&rdquo; friendship with a guy who has &ldquo;right foot&rdquo; and &ldquo;left foot&rdquo; socks</li>
</ol>


<p>Over the next 4 years I would keep going back to Jeff whenever something new
puzzled me, and he would keep giving me pointers that directed me down the
right path.  I <a href="http://bit.ly/pryisawesome">learned about Pry</a> from Jeff when
I was working on the <code>directoryservice</code> provider and couldn&rsquo;t figure out why my
variables had no value, my understanding on the principles of unit testing came
from completely screwing up spec tests, and my blog posts on type/provider
development never would have happened if I didn&rsquo;t make all those mistakes and
have someone help me learn from them.  So when I hit that point at Puppet where
I began thinking about moving on to &ldquo;the next big thing,&rdquo; Jeff was a natural
choice.</p>

<p>With that in mind, I&rsquo;m happy to announce that as of September 7th, 2017 I&rsquo;ll be
joining Jeff at <a href="openinfrastructure.co">openinfrastructure.co</a> where we&rsquo;ll be
available to consult on everything from DevOps practices, Puppet
deployments/module development/etc, and &ldquo;how you turn ordinary socks into
&lsquo;right foot socks&rsquo; and &lsquo;left foot socks.&rsquo;&rdquo; (I&rsquo;m MOSTLY kidding on the last bit,
but that&rsquo;s not my area of expertise, soooo&hellip;..)</p>

<p>It&rsquo;s been 6.5 years of consulting with Puppet Inc. and I&rsquo;m not planning on
stopping anytime soon. I&rsquo;m grateful for all the opportunities and experiences
that have come my way, and I&rsquo;m looking forward to going back to a smaller work
environment and more freedom to choose those opportunities! If you have one of
those opportunities and are looking for someone to help you out,
please look us up at
<a href="http://www.openinfrastructure.co">http://www.openinfrastructure.co</a> and let us
know about it!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Profiles and the Path to Hiera Data]]></title>
    <link href="http://garylarizza.com/blog/2017/08/24/data-escalation-path/"/>
    <updated>2017-08-24T20:41:25-05:00</updated>
    <id>http://garylarizza.com/blog/2017/08/24/data-escalation-path</id>
    <content type="html"><![CDATA[<p><em>This blog post first appeared on the Puppet blog as <a href="https://puppet.com/blog/hiera-data-and-puppet-code-your-path-right-data-decisions">Hiera, data, and Puppet code: your path to the right data decisions</a>, and is published here with permission of the Puppet blog editor.</em></p>

<p>The subject that generates the most questions for me from the Puppet community
is <a href="https://docs.puppet.com/hiera/">Hiera</a>. Not only do people want to know
what it is (a data lookup tool) and how to spell it (I before E except after
C), but even saying the word causes problems (It&rsquo;s HIGH-rah — two syllables,
with the accent on the first).</p>

<p>I get so many questions about Hiera because it&rsquo;s a tool for storing and
accessing site-specific data, and it&rsquo;s actually this problem of accessing data
within reusable code that most people are trying to solve. Many people think
the ONLY place data can live is within Hiera, but that&rsquo;s not always the case
(as we will see later with profiles). To help with these problems, I&rsquo;ve
identified all the ways that data can be expressed within Puppet, listed the
pros and cons of each, and made recommendations as to when each method should
be used.</p>

<p>For those people who are visual learners, here&rsquo;s a simplified flow chart below,
detailing the choices you need to make when deciding how to express your
configuration data.</p>

<p><a href="http://garylarizza.com/images/data_escalation_path.png"><img class="left" src="http://garylarizza.com/images/data_escalation_path.png"></a></p>

<h2>What is data and what is code?</h2>

<p>This issue of what constitutes data is the first wrinkle in devising what I
call a <strong>data escalation path</strong>. For background reading, the <a href="https://docs.puppet.com/pe/2017.2/r_n_p_intro.html">Puppet docs page
on roles and profiles</a> does
a great job of describing the difference between a component module and a
profile.</p>

<p>To quickly summarize: A component module is a general-purpose module designed
to model the configuration of a piece of technology (e.g., Apache, Tomcat or
ntpd), and a profile is an organization-specific Puppet module that describes
an organization&rsquo;s <em>implementation</em> of a piece of technology. (We also use the
term &ldquo;site-specific&rdquo; to refer to an organization&rsquo;s own particular data.)</p>

<p>For example, an Apache profile that an organization creates for itself might
use <a href="https://forge.puppet.com/puppetlabs/apache">the official Puppet Apache
module</a> to install and configure
Apache. But the profile might also contain resources for an organization&rsquo;s SSL
certificates or credentials, layered on top of the configuration provided by
the Puppet Apache module. The resource(s) modeling the SSL certificate(s) are
necessary <strong>only</strong> for that particular organization, which is why they don&rsquo;t
show up in the official Puppet Apache module.</p>

<p>In this example, the official Puppet Apache module itself represents the code,
or the generic and reusable aspect of the configuration (as any good component
module would). The profile contains the organizational (or site-specific) data
that is fed to the component module (or code) when that module is used. This
separation — and the fact that data can be represented within the same
constructs used to represent code — is frequently a source of confusion or
frustration for new Puppet users (and especially for users with a background in
object-oriented programming, which is almost antithetical to the
<a href="https://en.wikipedia.org/wiki/Declarative_programming">declarative</a> approach
that is core to Puppet).</p>

<p>Data within a profile can come in different forms:</p>

<ul>
<li>A variable assigned within the profile.</li>
<li>A Hiera/data lookup (done explicitly, or by way of the <a href="https://docs.puppet.com/puppet/4.10/hiera_automatic.html">automatic parameter lookup</a>).</li>
<li>A parameter&rsquo;s value when the profile is declared.</li>
</ul>


<p>With the above items all considered to be data, which option do you choose?
It&rsquo;s this question that the data escalation path will answer.</p>

<p><strong>NOTE</strong>: This post specifically covers data escalation paths within profiles,
and NOT within component modules. Unless explicitly noted, assume that
recommendations apply ONLY to profiles, and not component modules (since
profiles represent site-specific data).</p>

<h2>Why an escalation path?</h2>

<p>The decisions you make when writing Puppet manifests will seldom be plain and
obvious. Instead of focusing on whether something is in the right place, it&rsquo;s
better to think about the ways that complexity can help you solve problems.</p>

<p>You can absolutely put everything you would consider data inside Hiera, and
that would immediately provide you a way to handle most use cases. But the
legibility of your Puppet manifest suffers when you have to jump back to Hiera
every time you need to retrieve or debug a data value (which is a very
labor-intensive thing to do if you don&rsquo;t have direct access to the Puppet
masters). Plus, things like resource dependencies are particularly hard to
model in Hiera data, as opposed to using resource declarations within a class.</p>

<p>For simpler use cases, putting data into Hiera isn&rsquo;t necessary. But once you
reach a certain level of complexity, Hiera becomes extremely useful. I&rsquo;m going
to define those &ldquo;certain levels of complexity&rdquo; explicitly here, as well as both
the pros and the cons for each method of expressing data within your profiles.</p>

<h2>Hardcoding  variables</h2>

<p>The term &ldquo;hardcoding&rdquo; is wrapped in quotes here because traditionally the term
has negative connotations. When I refer to hardcoding, I&rsquo;m talking about
directly editing an item within a Puppet manifest, without assigning a
variable. In the example below, if you opened up the Puppet manifest and
changed the owner from &lsquo;root&rsquo; to &lsquo;puppet&rsquo;, that would be considered hardcoding
the value:</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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="nc">file</span> <span class="p">{</span> <span class="s1">&#39;/etc/puppetlabs/puppet/puppet.conf&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">file</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">group</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">mode</span>   <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppet:///modules/mymodule/puppet.conf&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Hardcoding has a negative connotation because typically, when someone would
hardcode a value in a script, it represented a workaround where a data item is
injected into the code — and mixing data and code means that your code is no
longer as generic and extensible as it once was.</p>

<p>That concern is still valid for Puppet: If you open up the official Puppet
Apache module and change or add a site-specific value within that component
module, then you ARE, in fact, mixing data with code. If instead you edit the
Apache profile for your organization and change a value in that profile, then
you&rsquo;re changing site-specific data in something that is already considered
site-specific. The difference is that the official Puppet Apache module is
designed to be extensible, and used where necessary, while the profile is meant
to be used only by your own organization (or site, or group).</p>

<p>Hardcoding a value is the easiest method to understand: Something that was
previously set to one value is now set to another value. It&rsquo;s also the easiest
change to implement — you simply change the value and move along. If done
correctly, someone could change the value without needing to understand the
Puppet DSL (domain specific language — i.e. the rules governing Puppet code in
a Puppet manifest). Finally, because it&rsquo;s simply text, a hardcoded value cannot
be overridden, and the value is exactly the same for all nodes.</p>

<h3>Pros</h3>

<ul>
<li>The easiest technique to understand: Something was changed from one value to another.</li>
<li>The easiest change to implement.</li>
</ul>


<h3>Cons</h3>

<ul>
<li>If you hardcode the same value in multiple places, then changing that value requires multiple individual changes.</li>
</ul>


<h3>Recommendations</h3>

<p>You should hardcode a value when:</p>

<ul>
<li>The value applies to EVERY NODE being managed by Puppet.</li>
<li>The value occurs once. If it occurs more than once within a manifest, use a variable instead.</li>
</ul>


<h2>Assigning a variable</h2>

<p>The next logical step after hardcoding a value is to assign a variable within a
Puppet manifest. Assigning a variable is useful when a value is going to be
used in more than one place within a manifest. Because variables within the
Puppet DSL cannot be reassigned, and because variables within a manifest cannot
be assigned or changed by Hiera, variables are considered private to the
implementation. This means they can be changed only by users with permission to
change Puppet manifests, not by people who are responsible for using the
console to pass data to the code written by manifest authors. So variables
really assist writers of Puppet code more than they assist consumers of Puppet
code.</p>

<p>Anytime there&rsquo;s a data value that will be expressed more than once within a
Puppet manifest, it&rsquo;s recommended that you use a variable. In the future, if
that value needs to be changed, all you need to do is change the variable&rsquo;s
value, and it will be updated wherever the variable was used.  Below is an
example of that concept in action:</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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="nv">$confdir</span> <span class="o">=</span> <span class="s1">&#39;/etc/puppetlabs/puppet&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${confdir}</span><span class="s2">/puppet.conf&quot;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">file</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">group</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">mode</span>   <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppet:///modules/mymodule/puppet.conf&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${confdir}</span><span class="s2">/puppetdb.conf&quot;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">file</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">group</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">mode</span>   <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppet:///modules/mymodule/puppetdb.conf&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Pros</h3>

<ul>
<li>Assigning a variable provides a single point within a manifest where data can be assigned or changed.</li>
<li>Assigning a variable within the DSL makes it visible to anyone reviewing the Puppet manifest. This means you don&rsquo;t need to flip back and forth between Hiera and Puppet to look up data values.</li>
</ul>


<h3>Cons</h3>

<ul>
<li>The value applies to EVERYONE — it must be changed if a different value is desired, and that change applies to everyone.</li>
<li>No ability to override a value.</li>
</ul>


<h3>Recommendations</h3>

<p>You should assign a variable when:</p>

<ul>
<li>The data value shows up more than once within a manifest.</li>
<li>The data value applies to EVERY node.</li>
</ul>


<h2>Conditionally assigning a variable</h2>

<p>In the previous section on assigning a variable, I recommend that variables be
used only when their value applies to EVERY node. But there is a way to work
around this: <strong>conditional statements</strong>.</p>

<p>Conditional statements in the Puppet DSL (such as <em>if</em>, <em>unless</em>, <em>case</em>, and
the selector operator) allow you to assign a variable once, but assign it
differently based on a specific condition. Using the previous example of
Puppet&rsquo;s configuration directory, let&rsquo;s see how that would be assigned
differently, based on the system&rsquo;s kernel fact:</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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="nv">$confdir</span> <span class="o">=</span> <span class="nv">$facts</span><span class="p">[</span><span class="s1">&#39;kernel&#39;</span><span class="p">]</span> <span class="err">?</span> <span class="err">{</span>
</span><span class='line'>  <span class="err">&#39;windows&#39;</span> <span class="err">=&gt;</span> <span class="err">&#39;C:\\ProgramData\\PuppetLabs\\puppet\\etc&#39;,</span>
</span><span class='line'>  <span class="err">default</span>   <span class="err">=&gt;</span> <span class="err">&#39;/etc/puppetlabs/puppet&#39;,</span>
</span><span class='line'><span class="err">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${confdir}</span><span class="s2">/puppet.conf&quot;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">file</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">group</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">mode</span>   <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppet:///modules/mymodule/puppet.conf&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${confdir}</span><span class="s2">/puppetdb.conf&quot;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">file</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">group</span>  <span class="p">=&gt;</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">mode</span>   <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppet:///modules/mymodule/puppetdb.conf&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Conditionally assigning a variable has its own section because when people
think about the choices they have for expressing data within Puppet, they
usually think of Hiera. Hiera is an excellent tool for conditionally assigning
a value, based on its internal hierarchy. But what if the conditional logic you
need to use doesn&rsquo;t follow Hiera&rsquo;s configured hierarchy? Your choices are to:</p>

<p>Edit Hiera&rsquo;s hierarchy to add the logic you need (which is potentially a
disruptive change to Hiera that will affect lookups), or Use conditional logic
within the DSL.</p>

<p>Since we&rsquo;re talking about an escalation path, conditionally assigning a
variable is the next logical progression when complexity arises.</p>

<h3>Pros</h3>

<ul>
<li>Values can be assigned based on whatever conditional logic is necessary.</li>
<li>Values are assigned within the Puppet DSL, and thus are more visible to Puppet code reviewers (versus reviewing Hiera data, which may be located elsewhere).</li>
<li>Reusability remains intact: The variable is assigned once, and used throughout the manifest.</li>
</ul>


<h3>Cons</h3>

<ul>
<li>Variables still cannot be reassigned or overridden.</li>
<li>Conditional logic can grow to become stringy and overly complex if left unchecked.</li>
<li>Conditional logic is syntax-heavy, and requires knowledge of the Puppet DSL (i.e., it&rsquo;s not something easily used by people who don&rsquo;t know Puppet).</li>
</ul>


<h3>Recommendations</h3>

<p>You should use conditional logic to assign a value within a profile when:</p>

<ul>
<li>The conditional logic isn&rsquo;t overly complex.</li>
<li>The conditional logic is different from the Hiera hierarchy.</li>
<li>Visibility of the data value within the Puppet DSL is a priority.</li>
</ul>


<h2>Hiera lookups and class parameters</h2>

<p>Puppet&rsquo;s data lookup tool is Hiera, and Hiera is an excellent way to model data
in a hierarchical manner based on layers of business logic. Demonstrating how
Hiera works is the easy part; implementing it (and knowing when to do Hiera
calls) is another story.</p>

<p>Before we get there, it&rsquo;s important to understand that Hiera lookups can be
done ad hoc <a href="https://docs.puppet.com/puppet/latest/lookup_quick.html">through the use of the <code>hiera()</code> or <code>lookup()</code> functions</a>,
or through the <a href="https://docs.puppet.com/hiera/latest/puppet.html#automatic-parameter-lookup">automatic class parameter lookup functionality</a>. The
previous links will give you detailed explanations. Briefly, if a class is
declared and a value is not explicitly assigned for any of the class&rsquo;s
parameters, Hiera will automatically do a lookup for the full parameter name.
For example, if the class is called &lsquo;<code>apache</code>&rsquo; and the parameter is called
&lsquo;<code>port</code>&rsquo;, then Hiera does an automatic parameter lookup for <code>apache::port</code>.</p>

<p>We&rsquo;ll get back to automatic parameter lookups in a second, but for now let&rsquo;s
focus on explicit lookups. Here&rsquo;s an example using both the older <code>hiera</code>
function and the newer <code>lookup</code> function:</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='puppet'><span class='line'><span class="nv">$apache_port</span>    <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;apache_port&#39;</span><span class="p">)</span>
</span><span class='line'><span class="nv">$apache_docroot</span> <span class="o">=</span> <span class="nf">lookup</span><span class="p">(</span><span class="s1">&#39;apache_docroot&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Explicit lookups using one of the above functions are easier to see and
understand when you&rsquo;re new to Puppet, because <a href="https://docs.puppet.com/hiera/latest/puppet.html#automatic-parameter-lookup">the automatic parameter
lookup</a> functionality is relatively hidden to you (should you not be aware
of its existence). More importantly, explicit lookups within a Puppet class are
considered to be private to that class. By &ldquo;private,&rdquo; I mean the
object-oriented programming definition: The data is limited in scope to this
implementation, and there&rsquo;s no other external way to override or affect this
value, short of changing what value Hiera ends up returning. You can&rsquo;t, for
example, pass in a parameter and have it take precedence over an explicit
lookup — the result of the lookup stands alone.</p>

<p>More than anything, the determining factor for whether you use an explicit
lookup or expose a class parameter to the profile should be whether the Hiera
lookup is merely a shorthand for getting a value that others SHOULDN&rsquo;T be able
to change, or whether this value should be exposed to the profile as part of
the API. If you don&rsquo;t want people to be able to override this value outside of
Hiera, then an explicit lookup is the correct choice.</p>

<h3>Explicit lookup pros</h3>

<ul>
<li>No need for conditional logic since Hiera is configured independently. Simply do a lookup for a value, and assign it to a variable.</li>
<li>Using a lookup function is a visible indicator that the data lives outside the DSL (in Hiera).</li>
</ul>


<h3>Explicit lookup cons</h3>

<ul>
<li>Loss of visibility: The data is inside Hiera&rsquo;s hierarchy, and determining the value requires invoking Hiera in some manner (as opposed to simply observing a value in the DSL).</li>
<li>If the lookup you want to perform doesn&rsquo;t conform to Hiera&rsquo;s existing hierarchy, then Hiera&rsquo;s hierarchy will need to be changed, which is disruptive.</li>
</ul>


<h3>Explicit lookup recommendations</h3>

<p>You should use an explicit data lookup when:</p>

<ul>
<li>The data item is private to the implementation of the class (i.e., not exposed as an API to the profile).</li>
<li>The value from Hiera should not be overridden within the Puppet DSL.</li>
</ul>


<h3>Class parameters</h3>

<h4>API vs. internal logic</h4>

<p>When building a profile, the implementation of the profile (i.e., anything
between the open and closing curly braces {} of a class definition:  <code>class
apache { … }</code> ) is considered to be private. This means that there really are
no guarantees around specific resource declarations as long as the technology
is configured properly in the end. Class parameters are considered to be part
of the profile&rsquo;s API, and thus there&rsquo;s a guarantee that existing parameters
won&rsquo;t be removed or have their functionality changed within a major release (if
you follow semantic versioning).</p>

<p>More specifically, exposing a parameter indicates to your Puppet code users
that this is something that can be set or changed. Think of computer hardware
and the differentiation between Phillips head screws and Torx screws. The
Phillips head screws usually mean that customer intervention is allowed, much
the same way that parameters indicate data values that can be changed, while
Torx screws usually mean that customer intervention is disallowed, much the
same way as variables or explicit lookups within a profile cannot be reassigned
or overridden.</p>

<p>As referenced in the previous section, <a href="https://docs.puppet.com/hiera/latest/puppet.html#automatic-parameter-lookup">this document on the automatic class
parameter lookup functionality</a> describes the order of precedence for
setting class parameters:</p>

<ol>
<li>Parameter values are explicitly set with a <a href="https://docs.puppet.com/puppet/latest/lang_classes.html#using-resource-like-declarations">resource-like class declaration</a>.</li>
<li>Puppet performs a Hiera lookup in the style of <code>(CLASS NAME)::(PARAMETER NAME)</code>.</li>
<li>The default value set in the class definition.</li>
</ol>


<p>By exposing a class parameter to your profile, you allow for the data to be
entered into Hiera without needing an explicit lookup in the profile.
Additionally, class parameters can be specified during a resource-like class
declaration that allows the user to override the Hiera lookup layer and pass in
their desired value. The user understands that class parameters are Puppet&rsquo;s
way of allowing input and altering the way Puppet configures the piece of
technology. In this way, class parameters aren&rsquo;t merely another method for
performing a Hiera lookup; they&rsquo;re also an invitation for user input.</p>

<h4>Discoverability and extensibility</h4>

<p>One important distinction with class parameters: The Puppet Enterprise console
is able to discover class parameters and present them visually. It can do this
because <a href="https://docs.puppet.com/puppetserver/2.4/puppet-api/v3/environment_classes.html">Puppet Server has an API that exposes this data</a>, and that
means parameters and classes can be queried and enumerated. Explicit Hiera
lookups are not discoverable in the same way; you will need to search through
your codebase manually.</p>

<p>Next, class parameters can have their values assigned by an external node
classifier, or ENC, but explicit Hiera lookups cannot. An ENC is an arbitrary
script or application that can tell Puppet which classes a node should have.
(For more information, <a href="https://docs.puppet.com/puppet/5.0/nodes_external.html#what-is-an-enc">refer to this document on ENCs</a>.)  For Puppet
Enterprise, the Puppet Enterprise console acts as an ENC.</p>

<p>Finally, consider the extensibility of explicit lookups versus class
parameters. Puppet introduced the <code>lookup()</code> function a while back as a
replacement for the <code>hiera()</code> function, which means that over time, all
<code>hiera()</code> function calls will need to be converted to <code>lookup()</code> function
calls. Class parameters have remained largely unchanged since their
introduction (with <a href="https://docs.puppet.com/puppet/4.10/lang_data.html">data types being an additional change</a>), so
people using class parameters and the automatic parameter lookup don&rsquo;t need to
convert all those explicit lookups. In this case, explicit lookups may require
more work than class parameters when performing an upgrade.</p>

<p>Because these two lookups have two fundamentally different purposes, I&rsquo;m
treating their usages separately.</p>

<h3>Class parameter lookup pros</h3>

<ul>
<li>Signals to users of Puppet code that this data item is configurable.</li>
<li>Allows the value to be assigned either by the Puppet Enterprise console (<a href="https://docs.puppet.com/puppet/5.0/nodes_external.html#what-is-an-enc">or other configured ENC</a>) or Hiera.</li>
<li>Classes and parameters are discoverable through the Puppet Server API.</li>
</ul>


<h3>Class parameter lookup cons</h3>

<ul>
<li>Automatic parameter lookup is unexpected if you don&rsquo;t know it exists.</li>
<li>Loss of visibility: The data is inside Hiera&rsquo;s hierarchy, and determining the value requires invoking Hiera in some manner (as opposed to simply  observing a value in the DSL).</li>
<li>Each parameter is unique, so even if multiple profiles expose a parameter of the same name that requires the same value, there needs to be a value in Hiera for each unique parameter.</li>
</ul>


<h3>Class parameter recommendations</h3>

<p>You should expose a class parameter when:</p>

<ul>
<li>You require the conditional logic within Hiera&rsquo;s hierarchy to determine the value of a data item.</li>
<li>If or when you need to override the value using the Puppet Enterprise console (or other configured ENC).</li>
<li>To indicate that this part of the profile is configurable to users of Puppet code.</li>
</ul>


<h2>Summary</h2>

<p>Writing extensible code and keeping configuration data separate are always in
the back of every Puppet user&rsquo;s mind, but the mechanics of how to achieve this
goal can seem daunting. With this post, I hope you now have a clearer path for
structuring your Puppet code!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Roles and Profiles in a Control Repo?]]></title>
    <link href="http://garylarizza.com/blog/2017/01/17/roles-and-profiles-in-a-control-repo/"/>
    <updated>2017-01-17T13:14:36-06:00</updated>
    <id>http://garylarizza.com/blog/2017/01/17/roles-and-profiles-in-a-control-repo</id>
    <content type="html"><![CDATA[<p>In the past, the thing that got me to make a blog post was answering a question
more than once and not having a good source to point someone to after-the-fact.
As the docs at <a href="http://docs.puppet.com">docs.puppet.com</a> have become more
comprehensive, I find that I&rsquo;m wanting to write about things infrequently. But,
all it takes is a question or two from a customer to kick things in the
ass and remind me that there&rsquo;s still a <em>LOT</em> of tribal knowledge around Puppet
(let alone the greater community). It&rsquo;s with THAT theme that we talk about
Roles &amp; Profiles, and the Control Repo.</p>

<p><a href="https://docs.puppet.com/pe/latest/cmgmt_control_repo.html">Like many things nowadays, there are official Puppet docs on the Control Repo.</a>
In a nutshell, the Control Repo is the repository that Puppet&rsquo;s Code Manager
(or R10k in the open source) uses to track Puppet Environments and the versions
of all Puppet modules within each Puppet Environment. On a greater scale, the
Control Repo is an organization&rsquo;s implementation of Puppet to the extent that
it can (and eventually will) fully represent your organization&rsquo;s
infrastructure. Changes to the Control Repo WILL incur changes to your Puppet
Master(s), and in most cases will also bubble down to your managed nodes
(i.e. if you&rsquo;re changing a profile that&rsquo;s being used by 1000 nodes, then that
change will be definitely change the file that&rsquo;s on each Puppet Master but will
also change the enforcement of Puppet on those 1000 nodes).</p>

<p><a href="https://docs.puppet.com/pe/latest/r_n_p_intro.html">Similarly, Roles &amp; Profiles has its own official docs page!</a>
As a recap, &ldquo;Role &amp; Profiles&rdquo; is a design pattern (that&rsquo;s all!) that has been
employed by Puppet Users for several years as a way to make sense of wiring
up public Puppet modules with site-specific implementations and data. It allows
organizations to share common modules while also having the ability to add their
own customizations and implement a separate configuration management data layer
(i.e. Hiera).</p>

<p>Both the Control Repo and Roles &amp; Profiles (R&amp;P) have undergone several evolutions to
get them to the reliable state we know today, and they&rsquo;ve had their shared
history: we&rsquo;ve implemented Roles &amp; Profiles both inside and outside the Control Repo&hellip;</p>

<h2>Roles and Profiles outside the Control Repo</h2>

<p>Roles &amp; Profiles were (was?) created before the Control Repo because the problem of
disentangling data from Puppet code was a greater priority than automating
code-consistency across multiple Puppet masters. When the idea of using a git
repo to implement dynamic Puppet Environments came along, the success of
being able to ensure module consistency across all your masters was pretty
landmark. The workflow, however, needed some work &ndash; there were a LOT of steps.
Git workflow automation loves it some hooks, and so the idea of a post-receive hook
that would immediately update a Puppet environment was a logical landing point.
The idea was that all modules would be listed and &lsquo;pinned&rsquo; to their correct
version/tag/commit within <code>Puppetfile</code> that lived at the root of the Control
Repo. &lsquo;Roles&rsquo; and &lsquo;Profiles&rsquo; are Puppet modules, modules were already listed
in <code>Puppetfile</code>, so some customers listed them there initially. During a code
deploy, R10k/Code Manager would read that file, pull down all the modules at their
correct versions, and then move along. That entire workflow looked like this:</p>

<ol>
<li>Create/Modify a Profile and push changes to the Profile module repo</li>
<li>Create a branch to the Control Repo and modify <code>Puppetfile</code> to target the new Profile changes</li>
<li>Push the Control Repo changes up to the remote (where the git webhook catches that change and deploys it to Puppet Masters)</li>
<li>Classify your node (if needed) and/or test the changes</li>
<li>If changes are necessary, go back to step 1 and repeat up to step 4</li>
<li>Once everything works, submit a Pull Request to the Control Repo</li>
</ol>


<p>This workflow works regardless of whether a Role or Profile was changed, but
the biggest thing to understand is that ONLY the Control Repo has the git webhook
that will deploy code changes to your Puppet Masters, so if you want to trigger
a code deploy then you&rsquo;ll need to change the Control Repo and push that change
up (or have access to trigger R10k/Code Manager on the Puppet Master). This
resulted in a lot of &lsquo;dummy&rsquo; changes that were necessary SOLELY to trigger a
code change. Conversely, changes to the Roles or Profiles module (they&rsquo;re separate)
don&rsquo;t get automatically replicated, so even if there&rsquo;s a small change to a
Profile you&rsquo;ll still need to either trigger R10k/Code Manager by hand or make
a small dummy commit to the Control Repo to trigger a code deploy.</p>

<p>As I said before, some customers implemented Roles &amp; Profiles and the Control Repo
this way for awhile until it was realized that you could save steps by putting
both the Roles and Profiles module into the Control Repo itself&hellip;</p>

<h2>Roles and Profiles inside the Control Repo</h2>

<p>Since the entire contents of the Control Repo are already cloned down to disk by
R10k/Code Manager, the idea came about to store the Roles and Profiles modules
in a special directory of the Control Repo (usually called &lsquo;site&rsquo; which is short
for &lsquo;site-specific modules&rsquo;), and then change <code>$modulepath</code> within Puppet to
look for the &lsquo;site&rsquo; folder within every Puppet Environment&rsquo;s directory path as
another place for Puppet modules to live. This worked for two reasons:</p>

<ol>
<li>It shortened the workflow (since changes to Roles and Profiles were done
within the history of the Control Repo, there was no need to change the
version &lsquo;pin&rsquo; inside <code>Puppetfile</code> as a separate step)</li>
<li>Because Roles and Profiles are now within the Control Repo, changes made to
Roles and Profiles will now trigger a code deploy</li>
</ol>


<p>For the vast majority of customers, putting Roles &amp; Profiles inside the Control
Repo made sense and kept the workflow shorter than it was before. It also had
the added benefit of turning the Control Repo into the important artifact that
it is today (thanks to the git webhook).</p>

<h2>Can/Should we also put other modules inside the Control Repo?</h2>

<p>Once you add the site directory to <code>$modulepath</code>, it opens up that directory to
be used as a place for storing ANY Puppet modules. The question then remains:
should the site directory be used for anything else other than Roles and Profiles?</p>

<p>Maybe?</p>

<p>Just like Puppet, though, just because you CAN do things, doesn&rsquo;t immediately
mean you SHOULD. It&rsquo;s important to understand that the Control Repo is
fundamental to ensuring code consistency across multiple Puppet Masters. For
that reason, commits to the Control Repo should be scruitinized closely. If
you&rsquo;re a large team with many Puppet contributers and many Puppet masters, then
it&rsquo;s best to keep modules within their own git repositories so multiple team
members can work independenly and the Control Repo can be used to &ldquo;tie it all
together&rdquo; in the end. If you&rsquo;re the only Puppet contributor, you&rsquo;re using 80%
of your modules from the Puppet Forge, but you have 3 relatively-static modules
outside of Roles and Profiles that you&rsquo;ve written specifically for your
organization and you want them in the site directory of the Control Repo then
you&rsquo;re probably fine. See the difference?</p>

<h2>Who owns what?</h2>

<p>One of the biggest factors to influence where Puppet modules should be managed
is the split of which teams own which decisions. Usually, Puppet infrastructure
is owned by an internal operations team, which means that the Ops team is used
to making changes to the Control Repo. If Puppet usage is wide enough within
your organization it&rsquo;s common to find application teams who own specific
Profiles that are separate from the infrastructure. It&rsquo;s usually easier to
grant an outside team access to a separate repo than it is to try and restrict
access to a specific folder or even branch of an existing repository, and so
in that case it might make sense to make the Profile module its own repository.
If the people that own the Puppet infrastructure are the same people that
make changes to Puppet modules, then it doesn&rsquo;t really matter where Roles and
Profiles go.</p>

<p>For many organizations this is THE consideration that determines their choice,
but remember to build a workflow for today with the ability to adapt to
tomorrow.  If you have a single person outside the ops team contributing to
Puppet, it doesn&rsquo;t mean that you need to upend the workflow just for them.
Moving from something like having Roles &amp; Profiles inside the Control Repo to
having them outside the Control Repo is an easy switch to implement (from
a technical standpoint), but the second you make that switch you&rsquo;re adding
steps to EVERYONE&rsquo;S workflow and changing the location of the most commonly
used modules within Puppet. That&rsquo;s a heavy cost &ndash; don&rsquo;t do it without reason.</p>

<h2>So what are the OFFICIAL RECOMMENDATIONS THEN?!?!</h2>

<p>We officially recommend you calm down with that punctuation. Beyond that, here it is:</p>

<ul>
<li>Put Roles &amp; Profiles within the site directory of the Control Repo unless you
have a specific reason NOT to.</li>
</ul>


<p>Do you have multiple Puppet contributors and separate modules for EACH
INDIVIDUAL Profile?  Then you might want to have separate repos for each
Profile and put them in <code>Puppetfile</code> to keep the development history separate.
You ALSO might want to put each individual Profile module in the site directory
of the Control Repo and just run with it that way. The bottom line here would
be access: who can/should access Profiles, who can/should access the Control
Repo, are those people the same, and do you need to restrict access for some
reason? Start with doing it this way and change WHEN YOU HIT THAT COMPLEXITY!
Don&rsquo;t deviate because you &lsquo;anticipate something&rsquo; &ndash; change when you&rsquo;re ready
for it and don&rsquo;t overarchitect early.</p>

<ul>
<li>If you&rsquo;re a smaller team with the same people who own the Puppet infrastructure
as who own Puppet module development and you have a couple of small internal modules
that don&rsquo;t change very often, AND putting them inside the &lsquo;site&rsquo; folder of
the Control Repo is easier for you than managing individual git repos, then by
all means do it!</li>
</ul>


<p>Whew that was a lot. Basically, yes, I&rsquo;ve outlined a narrow case because usually
creating a new git repository is a very small cost for an organization. If
you&rsquo;re in an organization where that&rsquo;s NOT the case, then the site directory
solution might appeal to you. What you gain in simplicity you lose in access
and security, though, so consider that ahead of time. Finally, the biggest factor
HERE is that the same people own the infrastructure and module code, so you can
afford to make shortcuts.</p>

<ul>
<li>Have an internal Puppet Policy/Style Guide for where Puppet modules &ldquo;go.&rdquo;</li>
</ul>


<p>If you&rsquo;ve had the conversation and made the decision, DOCUMENT IT! It&rsquo;s more
important to have an escalation path/policy for new Puppet users in your
organization to ensure consistency (the last thing you want to do is to keep
having this conversation every other month).</p>

<ul>
<li>Moving a module from the &lsquo;site&rsquo; directory to its own repository is not
difficult, but it does add workflow steps.</li>
</ul>


<p>Remember that if a module doesn&rsquo;t live in the &lsquo;site&rsquo; directory then it needs
to get &lsquo;pinned&rsquo; in <code>Puppetfile</code>, and that adds an extra step anytime that
module needs updated within a Puppet Environment.</p>

<h2>Summary</h2>

<p>First, if you&rsquo;ve read this post it&rsquo;s probably because you&rsquo;re Googling for
material to support your cause (or someone cited this post as evidence to back
their position). You might have even skipped down here for &ldquo;the answer.&rdquo; Guess
what &ndash; shit doesn&rsquo;t work like that! Storing Roles &amp; Profiles (and/or other
Puppet modules) within the &lsquo;site&rsquo; directory is an organizational choice based
on the workflow that best jives with an organization&rsquo;s existing developmental
cycle and ownership requirements. The costs/benefits for each choice boil down
to access, security, and saving time. The majority of the time putting Roles
&amp; Profiles in the Control Repo saves time and keeps all organizational-specific
information in one place.  If you don&rsquo;t have a great reason to change that,
then don&rsquo;t.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Workflows Evolved: Even Besterer Practices]]></title>
    <link href="http://garylarizza.com/blog/2015/11/16/workflows-evolved-even-besterer-practices/"/>
    <updated>2015-11-16T09:00:24-06:00</updated>
    <id>http://garylarizza.com/blog/2015/11/16/workflows-evolved-even-besterer-practices</id>
    <content type="html"><![CDATA[<p>It&rsquo;s nearly been two years since I posted the Puppet Workflow series and
several things have changed:</p>

<ul>
<li>R10k now ships with Puppet Enterprise and <a href="http://docs.puppetlabs.com/pe/latest/r10k.html">there are docs for it!</a></li>
<li>There&rsquo;s even a <a href="http://docs.puppetlabs.com/pe/latest/r10k_config_console.html"><code>pe_r10k</code> module</a> that ships with Puppet Enterprise 2015.2.x and higher to configure R10k</li>
<li><a href="https://github.com/puppetlabs/control-repo">Control repos are the standard and are popping up all over the place</a></li>
<li>Most people are bundling Hiera data with their Control repo (unless they have a very good reason not to)</li>
<li>Ditto for Roles and Profiles</li>
<li>The one-role-per-node rule is a good start, but PE&rsquo;s rules-based classification engine allows us to relax that rule</li>
<li>Roles still include Profiles, but conditional logic is allowed and recommended to keep Hiera hierarchy levels minimal</li>
<li>&lsquo;Data&rsquo; goes in Hiera, but the definition of &lsquo;data&rsquo; changes between organizations</li>
<li>There&rsquo;s now a (somewhat) defined path for whether &lsquo;data&rsquo; is included in a profile or Hiera</li>
<li>Automatic Parameter Lookup + Hiera&hellip;it&rsquo;s still hard to debug, but we&rsquo;re getting there</li>
<li>I&rsquo;m incredibly wary of taking Uber during peak travel times with rate multipliers</li>
</ul>


<p>It&rsquo;s been awhile since I&rsquo;ve had a good rant, so let&rsquo;s get right into it!</p>

<h2>Code Management with R10k</h2>

<p>As of PE 3.8, R10k became bundled with Puppet Enterprise (PE) and was referred
to as &ldquo;Code Management&rdquo; which initially confused people because the only thing
about PE that was changed was that the R10k gem was preinstalled into PE&rsquo;s Ruby
installation.  The purpose of this act was twofold:</p>

<ol>
<li>The Professional Services team was installing R10k in essentially EVERY services engagement, and so it made sense to ship R10k and thus officially support its installation</li>
<li>We&rsquo;ve always had plans to keep the functionality that R10k provided but not NECESSARILY the tool-known-as-R10k, so calling the service it provided something OTHER than R10k would allow us to swap out the implementation underneath the hood while still being able to talk about the functionality it provided</li>
</ol>


<p>Of course, if you didn&rsquo;t live inside Puppet Labs it&rsquo;s possible that you might not have gotten this
memo, but, hey: better late than never?</p>

<p>For various reasons, we also never initially shipped a PE-specific module to
configure R10k, so you ALSO had to either manually setup <code>r10k.yaml</code> or use
<a href="https://github.com/acidprime/r10k">Zack Smith&rsquo;s R10k module</a> to manage that file. Of course, that
module did all kinds of OTHER things (like installing the R10k gem, setting up
webhooks, and making my breakfast), which meant that if you used it with the
version of PE that shipped R10k, you had to be careful to use the version of
the module that didn&rsquo;t ALSO try to upgrade that gem on your system (and whoops
if the module actually upgraded the version of R10k that we shipped). This is
why that module is Puppet Approved but not an offical Puppet Labs module: it
does things that we would consider &ldquo;unsupported&rdquo; outside of a professional
services engagement (i.e. the webhook stuff). Finally, the path to
<code>r10k.yaml</code> was changed to <code>/etc/puppetlabs/r10k/r10k.yaml</code>, but, in its
absence, the old path of <code>/etc/r10k.yaml</code> would be used and a message would
be displayed to inform you of the new file path (in the case that both files
were present, the file at <code>/etc/puppetlabs/r10k/r10k.yaml</code> would win).</p>

<p>When PE version 2015.2.0 shipped (I&rsquo;m still not used to these version numbers
either, folks), we FINALLY shipped a <code>pe_r10k</code> module with similar structure to
Zack&rsquo;s R10k module &ndash; this meant you could FINALLY setup R10k immediatly without
having to install additional Puppet modules. Even better(er), in PE 2015.2.2 we
expose <a href="http://docs.puppetlabs.com/pe/latest/r10k_config_answers.html">a couple of PE installer answer file questions</a> that allow
you to configure R10k DURING INSTALL TIME &ndash; so now your servers could be
immediately bootstrapped with a single answers file (seriously, I know, it&rsquo;s
about time; I do this shit every week, you have no idea). It finally feels like
R10k has grown into the first-class citizen we all wanted it to be!</p>

<p>Which means it&rsquo;s time to dump it.</p>

<p>I kid. Mostly. The fact of the matter is that we&rsquo;re introducing a new service
to manage code within Puppet Enterprise, <a href="https://puppetlabs.com/blog/managing-infrastructure-as-code-now-easier-than-ever">and if you&rsquo;re interested in reading more about it, check out this blog post by Lindsay Smith about Code Manager.</a>
For you, the consumer, the process will be the same: you have a control
repo, you push changes, a service is triggered on your Puppet masters, and code
is synchronized on the Puppet master. What WILL change is the setup of this tool
(there will still be PE installer answer file questions that allow you to configure
this service, don&rsquo;t fret, and you&rsquo;ll still be able to configure this service through
a Puppet module, but the name of said module and configuration files on disk
will probably be different. Welcome to IT).</p>

<p>Be on the lookout for this service, and, as always, check out the <a href="http://docs.puppetlabs.com">PE docs site</a> for
more information on the Code Management service.</p>

<h2>Control (repo) freak</h2>

<p>With the explosion of R10k came the explosion of &ldquo;Control Repos&rdquo; all over the place.
Everyone had one, everyone had an opinion on what worked best, and, well, we didn&rsquo;t
really do a good job at offering a good startup control repo for you. Because of
that, <a href="https://github.com/puppetlabs/control-repo">we recently posted a &lsquo;starter&rsquo; control repo on Github</a> in the Puppet Labs
namespace that could be used to get started with R10k. Yes, it&rsquo;s definitely long
overdue, but there it is! I use it on all engagements I do with new customers, so
you can guarantee it&rsquo;ll have the use of Puppet Labs&#8217; PS team behind it. If you&rsquo;ve
not started with R10k yet (or if you have but you wanna see what kinda crazy shit
we&rsquo;re doing now), check it out. It&rsquo;s got great stuff in there like a config_version
script to spit out the most recent commit of the current branch of the control repo
(read: also current Puppet environment) as the &ldquo;Config Version&rdquo; string that Puppet
prints out during every Puppet run (<a href="https://docs.puppetlabs.com/puppet/latest/reference/config_file_environment.html#configversion">see here for more info on this functionality</a>).
We&rsquo;re also slowly adding things like inital bootstrapping profiles that will do
things like configure R10k/Code Manager, manage the SSH key necessary to contact
the control repo (should you be using an internal git repository server and
also require an SSH key to access that repo), and so on. Star that repo and keep
checking back, especially around PE releases, to see if we&rsquo;ve updated things in
a way that will help you out!</p>

<h2>&ldquo;Just put it in the control repo&rdquo;</h2>

<p>Look, if there&rsquo;s one thing that my blog emphasizes (other than the fact that I&rsquo;ve
got a hairpin trigger for cursing and an uncomfortable Harry Potter fetish) it&rsquo;s
that &ldquo;best practices&rdquo; are inversely related to the insecurities of the speaker.
Fortunately, I have no problem saying when I&rsquo;m wrong. If you&rsquo;ve got the time,
allow me my mea culpa moment. In the past I had recommended:</p>

<ul>
<li>Using a separate git repo for Hiera data</li>
<li>Using separate git repos for Roles and Profiles</li>
<li>The Dave Matthews Band</li>
</ul>


<p>Time, experience, and the legalization of recreational marijuana in Oregon have
helped me see the error in my ways (though, look, #41 is a good goddamn song,
especially on the Dave &amp; Tim Live at Luther College album), so allow me to provide
some insight into WHY I&rsquo;ve reconsidered my message(s)&hellip;</p>

<h3>Hiera Data</h3>

<p>In the past, I recommended a separate git repo for Hiera data along with
a separate entry in <code>r10k.yaml</code> that would allow R10k to clone the Hiera data repo
along the same vein as the control repo. The pro was that a separate Hiera data
repo would afford you different access rights to this repo as you would the
control repo (especially if different people needed different access to each
function). The con was that now the branch structure of your Hiera data repo
needed to EXACTLY MIRROR the structure of your control repo&hellip;.even if certain
branches had EXACTLY THE SAME Hiera data and no changes were necessary.</p>

<p>Puppet has enough moving parts, why did we need to complicate this if most
people didn&rsquo;t care about access levels differing between the two repos? The
solution was to bundle the Hiera data inside the control repo all the way up
until you had a specific need to split it out. Truth be told both methods
work with Puppet, so the choice is up to you (read: I DON&rsquo;T CARE WHICH METHOD
YOU USE OH MY GOD WILL YOU QUIT TRYING TO PICK A FIGHT WITH ME OVER THIS LOL) :)</p>

<p>Finally, there&rsquo;s an added benefit of putting this data inside the control repo,
and it&rsquo;s ALSO the reason for the next recommendation&hellip;</p>

<h3>Roles and Profiles</h3>

<p>This is one that I actually fought when someone suggested it&hellip;I even started to
recommend that a customer NOT do the thing I&rsquo;m about to recommend to you until they
very eloquently explained why they did it. In the end, they were right, and I&rsquo;m
passing this tip on to you:  Unless you have a very specific reason NOT to,
put your &lsquo;roles&rsquo; and &lsquo;profiles&rsquo; modules in your control repo.</p>

<p>Here&rsquo;s the thing about the control repo &ndash; you can set a post-receive hook on
the repository (or setup a Jenkins/Bamboo/whatever job) that will update all your
Puppet masters whenever changes are pushed to the remote git repository (i.e.
your git repository server). This means that anytime the control repo is updated
your Puppet masters will be updated. That&rsquo;s why it&rsquo;s CALLED the control repo &ndash; it
effectively CONTROLS your Puppet masters.</p>

<p>Understanding THAT, think about when you want your Puppet masters updated? Well,
you usually want to update them when you&rsquo;re testing something out &ndash; you made a
change to a couple of modules, then a profile (and possibly also a role), and
now you wanna see if that code works on more than just your local laptop.
But the Puppet landscape has changed a bit as the Puppet Forge has matured &ndash; most
people are using modules off the Forge and are at least TRYING not to use their
own component modules. This means that changes to your infrastructure are being
controlled from within roles/profiles. But even IF you&rsquo;re one of those people
who aren&rsquo;t using the Forge or who have to update an internal component module,
you&rsquo;re probably not wanting to update all your Puppet masters every time you
update a component module. There&rsquo;s probably lots of tinkering there, and every
change isn&rsquo;t &ldquo;update-worthy&rdquo;. Conversely, changes to your profiles probably
ARE &ldquo;update-worthy&rdquo;: &ldquo;Okay, let&rsquo;s pull this bit from Hiera, pass it as a parameter,
and now I&rsquo;m ready to check it out on a couple of machines.&rdquo;</p>

<p>If your roles and profiles modules are separate from your control repo, you
end up having to push changes to, say, a class in the profiles module, then
updating the Puppetfile in the control repo, then trigger an R10k run/sync.
If things aren&rsquo;t correct, you end up changing the profile, pushing that change
to the profile repo, and THEN having to trigger an R10k run/sync (and if you
don&rsquo;t have SSH access to your masters, you have to make a dummy commit to the
control repo so it triggers an R10k run OR doing a curl to some endpoint that
will update your Puppet master for you). That last step is the thing that ends
up wasting a bit of your time: why do we need to push a profile and then manually
do an R10k run of we&rsquo;ve established that roles and profiles will pretty much
ALWAYS be &ldquo;update-worthy&rdquo;? We don&rsquo;t. If you put the roles and profiles module
inside the control repo, then it will automatically update your Puppet masters
every time you make a change to one or the other. Bam &ndash; step saved. ALSO, if
you do this, you can take Roles/Profiles out of Puppetfile, which means you
no longer need to pin them! No more will you have to tie that module to a topic
branch during development time: just create a branch of the control repo and
go to town!  Wow, that saves even more time! I&rsquo;m uncomfortable with this level
of excitement!</p>

<p>The one thing you WILL need to do is to update <code>environment.conf</code> so that it
knows to look for the roles/profiles modules in a different path from all the
other modules (because removing it from Puppetfile means that it will no longer
go to the same modulepath as every other module managed inside Puppetfile).
For the purposes of cleanliness, we usually end up putting both roles/profiles
inside a <code>site</code> folder in the control repo. If you do that, your modulepath
in <code>environment.conf</code> looks a little something like this:</p>

<figure class='code'><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=''><span class='line'>modulepath = site:modules:$basemodulepath</span></code></pre></td></tr></table></div></figure>


<p>This means that Puppet will look for modules first in the &lsquo;site&rsquo; directory of
its current environment (this is the directory where we put roles/profiles),
and then inside the &lsquo;modules&rsquo; directory (this is where modules managed in Puppetfile
are cloned by default), and then in $basemodulepath (i.e. modules common to all
environments and also modules that Puppet Enterprise ships).</p>

<p>LOOK, BEFORE YOU FREAK OUT, YES, SITE COMES FIRST HERE, AND OTHER PEOPLE HAVE
SITE COME SECOND! Basically, if you have roles/profiles in the &lsquo;site&rsquo; directory
AND you manage to still have the module in Puppetfile, then the module in the &lsquo;site&rsquo;
directory will win. Feel free to flip/flop that if you want.</p>

<p><strong>TL;AR: (yes, you already read all of this so it&rsquo;s futile) put roles/profiles
inside the site directory of the control repo to save you time, but also don&rsquo;t
do it if you have a specific reason not to&hellip;or if you like being contrarian.</strong></p>

<h3>Dave Matthews</h3>

<p>The &ldquo;Everyday&rdquo; album was the &ldquo;jump the shark&rdquo; moment for the Dave Matthews band,
while the leaked &ldquo;Lillywhite Sessions&rdquo; that would largely make it to &ldquo;Busted Stuff&rdquo;
definitely indicated where the band wanted to go. They never recovered after that,
and, just like Boone&rsquo;s Farm &lsquo;wine&rsquo;, I stopped partaking in them.</p>

<p>Also, not ONCE did being able to play most every Dave Matthews song on the
acoustic guitar ever get me laid&hellip;though I can&rsquo;t tell exactly whose fault that
was. On second thought, that was probably me. Though Tim Reynolds is an absolute
beast of a musician; I&rsquo;m still #teamtim.</p>

<h2>One role per node, until you don&rsquo;t want to</h2>

<p>Why do we even make these rules if you&rsquo;re not gonna follow them? It&rsquo;s getting
awfully &ldquo;Who&rsquo;s Line Is It Anyways?&rdquo; up in here. Before PE 3.7, and its
rules-based classification engine, we recommended not assigning more than one
role to a node.  Why? Well, the Puppet Enterprise Console around that time
wasn&rsquo;t the best at tracking changes or providing authentication around tasks
like classification.  This meant if you tried to manage ALL of your
classification within the console you could have a hard time telling when
things changed or why. Fortunately, git provides you with this functionality.
Because of that, we (and when I say &lsquo;we&rsquo; I mean &lsquo;everyone in the field trying
to design a Puppet workflow that not only made sense but also had some level of
accountability&rsquo;) tried to displace most classification tasks from the Console
into flat files that could be managed with git. This is largely the impetus for
Roles and Profiles when you think about it: Profiles connect Puppet to external
ata and give you a layer to express dependencies between multiple Puppet
classes, and Roles is a mechanism for boiling down classification to a single
unit.</p>

<p>Once we launched a new Node Classifier that had a rules-based classification
engine AND role-based authentication control, we became more comfortable
delegating some of these classification tasks BACK to the console. The Node
Classifier ALSO made it easy to click on a node and not only see what was
classified to that node, but also WHERE it got that bit of classification
from (&ldquo;This node is getting the JBoss profile because it was put into the
App Servers nodegroup&rdquo;). With that level of accountability, we could start
relaxing our &ldquo;One Role Per Node™&rdquo; mandate, OR eliminate the roles module
altogether (and use nodegroups in the Node Classifier in place of roles).</p>

<p>The goal has always been to err on the side of &ldquo;debugability&rdquo; (I like making words).
I will usually try to optimize a task for tracing errors later, because I&rsquo;ve been
a sysadmin where the world is falling apart around you and you need to quickly
determine what caused this mess. Using one role per node makes sense if you
don&rsquo;t use a node classifier that gives you this flexibility, but MIGHT not if
you DO use a classifier that has some level of accountability.</p>

<h2>Roles, conditional logic, Hiera, and you</h2>

<p>Over time as I&rsquo;ve talked to people that ended up building Puppet workflows
based on the things I&rsquo;ve written (which still feels batshit crazy to me,
by the way, since I&rsquo;ve known myself for over 34 years), I&rsquo;ve noticed that people
seem to take the things I say VERY LITERALLY. And to this I say: &ldquo;You should
probably send me money via Paypal.&rdquo; Also &ndash; note that I&rsquo;m writing these things
to address the 80% of people out there using/getting started with Puppet. You
don&rsquo;t HAVE to do what I say, especially if you have a good reason not to, and
you SHOULDN&rsquo;T do what I say, especially if you&rsquo;re the one that&rsquo;s going to stay
with that organization forever and manage the entire Puppet deployment. For
everyone else out there, let&rsquo;s talk some more about roles.</p>

<p>The talking points around roles has always been &ldquo;Roles include profiles; that&rsquo;s it.&rdquo;
Again, going back to the idea that roles exist to support classification, this
makes sense &ndash; you don&rsquo;t want to add resources at a very high level like a roles
class because, well, honestly, there&rsquo;s probably a better place for it, but any
logic added to simply classification is a win.</p>

<p>Consider an organization that has both Windows and Linux application servers.
The question of whether to have separate roles for Linux and Windows
application servers is always one of the first questions to be surfaced. At
a low level, everything you do in a Puppet manifest is solely for the
purpose of getting resources into the catalog (a JSON object containing
a list of all resource Puppet is to be managing ond their desired end-state).
Whether you have two different roles matters not to Puppet so long as the
right node gets the right catalog. For a Puppet developer writing code, having
two separate roles also might not matter (and, in reality, based on the amount
of code assigned to either role, it might be cleaner to have different roles
for each). For the person in charge of classifying nodes with their assigned
role, it&rsquo;s probably easier to have a single role (<code>roles::application_server</code>, for example)
that can be assigned to ALL application servers, and then logic inside the role
to determine whether this will be a Windows application server using IIS or
a Linux application server using JBoss (or, going further, a Linux application
server running Weblogic, or Websphere, or Tomcat, whatever). Like we mentioned
in the previous point, if you&rsquo;re using the &ldquo;One role per node&rdquo; philosophy, then
you probably want a single role with conditional logic to determine Windows/Linux,
and then determine Tomcat/JBoss, and so on. If you&rsquo;re using the Puppet Enterprise
Console&rsquo;s node classifier, and thus the rule-based engine, you can afford not
to care about the number of node groups you create because you can create a rule
to match for application servers, and then a rule to match on operating system,
and create as many rules as you want to dynamically discover and classify nodes
on the fly.</p>

<p>The point here is that the PURPOSE of the Role is to aid classification, and
the focus on creating a role is to start small, use conditional logic to
determine which profiles to include, and then simply include them. If that
conditional logic uses Facter facts, awesome. If you need to look at a variable
coming from the Console to do the job, fine &ndash; go for it! But if you&rsquo;re using
the Role as a substitute for a Profile (i.e. data lookups, declaring classes,
even declaring resources), then you&rsquo;re probably going down a path that&rsquo;s gonna
make it confusing for people follow what&rsquo;s going on.</p>

<p>Bottom line: technology-agnostic roles that utilize conditional logic around
including profiles is a win, but keep tasks like declaring resources and
component modules to Profiles. Doing this provides a top-down path for
debugging and a cleaner overall Puppet codebase.</p>

<h2>What the hell is &lsquo;Data&rsquo; anyhow?</h2>

<p>This point has single-handedly caused more people to come up and argue with me.
I&rsquo;m not kidding. I shit you not, I&rsquo;ve had people legitimately *SCREAM* at me
about how wrong I was with my opinions here. The cool thing is that people LOVE
the idea of Hiera &ndash; it lets you keep the business-specific data out of your
Puppet manifests, it&rsquo;s expressed in YAML and not the Puppet DSL, and when it
works, it&rsquo;s magical.</p>

<p>The problem is that it&rsquo;s fucking magical. Seriously.</p>

<p>So what IS a good use of Hiera? Anytime you have a bit of data that is subject
to override (for example: the classical NTP problem where everyone should use
the generic company NTP server, except nodes at this location should use a
different NTP server, and this particular node should use ITSELF as its NTP
server), that bit of data goes into Hiera (and by &lsquo;that bit of data&rsquo;, I mean
&lsquo;the value of the NTP server&rsquo; or &lsquo;the NTP server&rsquo;s FQDN&rsquo;), which would look
SOMETHING like this:</p>

<figure class='code'><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='yaml'><span class='line'><span class="l-Scalar-Plain">ntpserver</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">pool.ntp.org</span>
</span></code></pre></td></tr></table></div></figure>


<p>What does NOT go into Hiera is a hash-based representation of the Puppet
resource that would then be passed to create_resources() and used to create
the resource in the catalog&hellip;which would look something like this:</p>

<figure class='code'><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='yaml'><span class='line'><span class="l-Scalar-Plain">ntpfiles</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="s">&#39;/etc/ntp/ntpd.conf&#39;</span><span class="p-Indicator">:</span>
</span><span class='line'>    <span class="l-Scalar-Plain">ensure</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">file</span>
</span><span class='line'>    <span class="l-Scalar-Plain">owner</span><span class="p-Indicator">:</span>  <span class="l-Scalar-Plain">0</span>
</span><span class='line'>    <span class="l-Scalar-Plain">group</span><span class="p-Indicator">:</span>  <span class="l-Scalar-Plain">0</span>
</span><span class='line'>    <span class="l-Scalar-Plain">mode</span><span class="p-Indicator">:</span>   <span class="l-Scalar-Plain">0644</span>
</span><span class='line'>    <span class="l-Scalar-Plain">source</span><span class="p-Indicator">:</span> <span class="s">&#39;puppet:///modules/ntp/ntpd.conf&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>&hellip;which would then be passed into Puppet like this:</p>

<figure class='code'><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='puppet'><span class='line'><span class="nf">create_resources</span><span class="p">(</span><span class="s1">&#39;file&#39;</span><span class="p">,</span> <span class="nf">hiera_hash</span><span class="p">(</span><span class="err">&#39;</span><span class="ss">ntpfiles</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>Yes, this is an exaggeration based on a very narrow use case, but what I&rsquo;m trying
to highlight is that the &lsquo;data&rsquo; bit in all that above mess is SOLELY an FQDN,
and everything else is arguably the &ldquo;Model&rdquo;, or your Puppet code.</p>

<p>Organizations LOVE that you can put as much &ldquo;stuff&rdquo; into Hiera as you want and
then Puppet can call Hiera, create resources based on what it tells you, and
merrily be on your way. Well, they &ldquo;love&rdquo; it until it doesn&rsquo;t work or does
something unexpected, and then debugging Hiera is a right bastard.</p>

<p>Understand that the problem I have would be with unexpected Hiera behavior. If
you&rsquo;re skilled in the ways of the Hiera and its (sometimes cloudy) interaction
with Puppet, then by ALL means use it for whatever ya like. BUT, if you&rsquo;re
still new to Puppet, then you may have a very loose mental map for how Hiera
works and where it interacts with Puppet&hellip;and nobody should have to have that
advanced level of knowledge just to debug the damn thing.</p>

<p>The Hiera + create_resources() use above is of particular nastiness simply
because it turns your Hiera YAML files into a potential mechanized weapon of Puppet
destruction.  If I know that you&rsquo;re doing this under the hood, I could
POTENTIALLY slip data into Hiera that would end up creating resources on a node
to do what I want. Frequently Puppet code is more heavily scrutinized than
Hiera data, and I could see something like this getting overlooked (especially
if you don&rsquo;t have a ton of testing around your Puppet code before it gets
deployed).</p>

<p>The REASON why create_resources() was created was because Puppet lacked the
ability to do things like recursion and loops inside the DSL, and sometimes
you WANT to automate very repeated tasks. Consider the case where you truly
DON&rsquo;T know how many of something is going to be on a node ahead of time &ndash; maybe
you&rsquo;re using VMware vRO/vRA and someone is building a node on-the-fly with
the web GUI. For every checkbox someone ticks there will be another application
to be installed, or another series of firewall rules, or SOMETHING like that.
You can choose to model these individually with profiles, OR, if the task is
repetitive, you can accept their choices as data and feed it back into Puppet
like a defined resource type. In fact, most use-cases for Hiera + create_resources()
is passing data into a defined resource type. As of Puppet 4.x.x, we have
looping constructs inside the DSL, so we can finally AUTOMATE these tasks
without having to use an extra function (of course, in THIS use case, whether
you use recursion/looping in the DSL or create_resources() matters not &ndash; you
get the same thing in the end).</p>

<p>For one last point, the Puppet DSL is still pretty easy to read (as of right now),
and most people can follow what&rsquo;s going on even if they&rsquo;re NOT PuppEdumicated.
Having 10 resource declarations in a row seems like a pain in the ass to write
when you&rsquo;re doing it, but READING it makes sense. Later on, if you need to know
what&rsquo;s going on with this profile, you can scan it and see exactly what&rsquo;s there.
If you start slipping lots of data into Hiera and looping logic into the DSL,
you&rsquo;re gonna force the person who manages Puppet to go back and forth between
reading Hiera code, then back to Puppet code, then back to the node, and so on.
Again, it&rsquo;s totally possible to do now, and frequently NECESSARY when you have
a more complex deployment and well-trained Puppet administrators, but initially
it&rsquo;s possible to build your own DSL to Puppet by slipping things into Hiera and
running away laughing.</p>

<p>So when do I put this &lsquo;data&rsquo; into the Profile and when is a good time to put it
into Hiera?  I&rsquo;m glad you asked&hellip;</p>

<h2>A path to Hiera data</h2>

<p>These last two points I&rsquo;ve written about before. I may be repeating myself, but
bytes are cheap. Like I wrote above (and before), putting data directly into a
Profile is the easiest and most legible way of providing &ldquo;external data&rdquo; into
Puppet. Yes, you&rsquo;ll argue, putting the data into a Profile, which is Puppet code,
is ARGUABLY NOT being very &ldquo;external&rdquo; about it. In my opinion it is &ndash; your Profile
is YOUR IMPLEMENTATION of a technology stack, and thus isn&rsquo;t going to be shared
outside your organization. I consider that external to all the component modules
out there, but, again, potato/potato. I recommend STARTING HERE when you&rsquo;re getting
started with Puppet. Hiera comes in when you have a very clear-cut need for
overriding data (a la: this NTP server everywhere, except here and here). The second
you might need to have different data, you can either start building conditional logic
inside the Profile, OR use the conditional logic that Hiera provides.</p>

<p>So &ndash; which do you use?</p>

<p>The point of Hiera is to solve 80% or better of all conditional choices in your
organization. Consider this data organization model:</p>

<ul>
<li>Everyone shares most of the same data items</li>
<li>San Francisco/London do their own things sometimes</li>
<li>Application tiers get their own level for dev/test/qa/prod-specific overrides</li>
<li>Combinations of tiers/locations/and business units want their own overrides</li>
<li>Node specific data is the most specific (and least-used) level</li>
</ul>


<p>If you&rsquo;re providing some data to Puppet that follows this model, then cool
&ndash; use Hiera. What about specific &ldquo;exceptions&rdquo; that don&rsquo;t fit this model? Do you
try to create specialized layers in Hiera just for these exceptions? Certain
organizations absolutely do &ndash; I see it all the time. What you find is that
certain layers in Hiera go together (this location/tier/business_unit level
goes right above location/tier, which goes right above location), and we
start referring to those coupled layers as &ldquo;Chains&rdquo;. Chains are usually tied
to some specific need (deploying applications, for example). Sometimes you
create a chain just to solve a VERY SPECIFIC hard problem (populating
<code>/etc/sudoers</code> in large organizations, for example).</p>

<p>The question is &ndash; do I create another &ldquo;Chain&rdquo; of layers in the hierarchy
solely because deploying <code>sudoers</code> is hard, or do I throw a couple of case
statements into the <code>sudoers</code> profile and keep it out of Hiera altogether?</p>

<p>My answer is to start with conditional logic in the <code>sudoers</code> profile and break
it out into Hiera if you see that &ldquo;Chain&rdquo; being needed elsewhere. Why? Because, like
I&rsquo;ve said many times before, debugging Hiera kinda sucks right now &ndash; there&rsquo;s no
way currently to get a dump of all variables and parameters for a particular node
and determine which were set by Hiera, which were set with variables in the DSL, which
came out of the console, and so on. If we HAD that tool, I&rsquo;d be all about using
it and polluting your hierarchy all day long (I expand upon this slightly in the
next point about the Automatic Parameter Lookup + Hiera).</p>

<p>Bottom line: Start with the data in the Profile, then move it to Hiera when you
need to override. Start with conditional logic in the Profile, then create a
&ldquo;Chain&rdquo; in the Hierarchy if you need to use it in more than one place.</p>

<h2>Hiera, APL, Refactoring, WTF</h2>

<p>Like I said, I&rsquo;ve written about this before. I like the Automatic Parameter
Lookup functionality in Puppet &ndash; it&rsquo;s ace. I like Hiera. But if you don&rsquo;t know
how it works, or that it exists, it feels too much like Magic. There are certain
things in the product that can ONLY be set by putting data inside Hiera and running
Puppet, and that is truly an awesome thing: just tell a customer &ldquo;drop this bit
of data somewhere in Hiera, run Puppet, and you&rsquo;re all set.&rdquo; But, again, if you
need to know how a particular line got into a particular config file on your
node, and it was set with the APL, then you&rsquo;ve got some digging to do.</p>

<p>There&rsquo;s still no tool, like I mentioned in the last item, to give me full
introspection into all variables/parameters set for a node and that
variable/parameter&rsquo;s origin.  Part of the reason as to WHY this tool doesn&rsquo;t
exist is because the internals of Puppet don&rsquo;t necessarily make it easy for you
to determine where a parameter/variable was set.  That&rsquo;s OUR problem, and
I feel like we&rsquo;re slowly making progress on marking these things internally so
we can expose them to our customers. Until then, you have to trace through code
and Hiera data.</p>

<p>I know the second I publish and tweet about this, I&rsquo;m gonna get a message from
R.I. Pienaar saying that I&rsquo;ve crazy for NOT pushing people toward using Hiera
more with the Automatic Parameter Lookup, because the more we use it, the faster
we can move away from things like params classes, and profiles, and everything
else, but the reality is I&rsquo;m ALL ABOUT PEOPLE using it if they know how it works.
I&rsquo;m ACTUALLY fucking happy that it works well for you &ndash; please continue to use
it and do awesome Puppet things. I only recommend to people who are getting
started to NOT USE it FIRST, and then, when you understand how it would help
you by clocking some hours of Puppet code writing and debugging, do some refactoring
and move to it!</p>

<p>Yes, refactoring is involved.</p>

<p>Look, refactoring is a way of life. You&rsquo;re gonna re-tool your Puppet code for
the purposes of legibility, or efficiency, or any of the many other reasons why
you refactor code &ndash; it&rsquo;s unavoidable. Also, if I come into your org and setup
Puppet for the most efficient use-case, and then I leave that into your
relatively-new-to-Puppet hands, it&rsquo;s probably not gonna be the best situation
because you won&rsquo;t have known WHY I made the decisions I did (and, even if I
document them, you might have gaps of knowledge that would help you understand
the problems I&rsquo;m helping you avoid).</p>

<p>Sometimes hitting the problem so you have first-hand knowledge of why you need
to avoid it in the future isn&rsquo;t the WORST thing in the world.</p>

<p>To move to any configuration management system means you&rsquo;re gonna be
refactoring.  Embrace it. Start small, get things working, then clean it up.
Don&rsquo;t try to build the &ldquo;fortress of sysadmin perfection&rdquo; with your first bit of
Puppet code &ndash; just get shit done! Allow yourself time during the month simply
to unwind some misgivings you realize after-the fact, and definitely seek
advice before doing something you feel might be particularly complex or
overarching, but getting shit done is gonna trump &ldquo;not working&rdquo; any day (or
whatever the manager-y buzzspeak is this week).</p>

<p>Bottom Line: APL if you understand it, start small, get shit done, refactor, repeat</p>

<h2>Hopefully this leads to more posts</h2>

<p>Holy shit, you&rsquo;re still reading?! Ohh, you skimmed down this far to see how long
this post was gonna be &ndash; got it. Either way, I&rsquo;m glad I finally got this out there.
It&rsquo;s been months, yes, but that doesn&rsquo;t mean I haven&rsquo;t been writing. We&rsquo;ve been
doing lots of internal work to try and get more official docs out to you and
less of &ldquo;Go read Gary&rsquo;s blog!&rdquo; You&rsquo;ll notice <a href="http://docs.puppetlabs.com/pe/latest/r10k.html">R10k has some official docs, right?!</a>
Yeah, that&rsquo;s awesome! We want more of that. BUT, there&rsquo;s still going to be times
where I feel like what I&rsquo;m gonna say isn&rsquo;t necessarily the &ldquo;party line&rdquo;, and that&rsquo;s
what this blog is about.</p>

<p>Thanks to everyone at Puppetconf and beyond who approached me and told me how
much they love what I write. I&rsquo;m gonna be humble as fuck in person, but I really
do get excited whenever someone says that. It&rsquo;s also crazy as hell when someone
from Wal-mart approaches you and says they built part of their deployment based
on the shit you wrote. From a guy who came from a town in Ohio with a population
of less than 8000 people, it&rsquo;s crazy to see where you&rsquo;re &ldquo;recognized.&rdquo;</p>

<p>So thank you, again, for all the support.</p>

<p>And sorry, Dave Matthews &ndash; it&rsquo;s not you, it&rsquo;s me. Actually, that&rsquo;s a lie; it was you.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet Workflows 4: Using Hiera in Anger]]></title>
    <link href="http://garylarizza.com/blog/2014/10/24/puppet-workflows-4-using-hiera-in-anger/"/>
    <updated>2014-10-24T08:13:49-05:00</updated>
    <id>http://garylarizza.com/blog/2014/10/24/puppet-workflows-4-using-hiera-in-anger</id>
    <content type="html"><![CDATA[<p>Hiera. That thing nobody is REALLY quite sure how to say (FYI: It&rsquo;s pronounced
&lsquo;hiera&rsquo;), the tool that everyone says you should be using, and the tool that
will make you hate YAML syntax errors with a passion. It&rsquo;s a data/code
separation dream, (potentially) a debugging nightmare, and absolutely vital in
creating a Puppet workflow that scales better than your company&rsquo;s Wifi strategy
(FYI: your company&rsquo;s Wifi password just changed. Again. Because they&rsquo;re not
using certificates). I&rsquo;ve already written a GOOD AMOUNT on why/how to use it,
but now I&rsquo;m going to give you a couple of edge cases. Call them &ldquo;best
practices&rdquo; (and I&rsquo;ll cut you), but I like to call it &ldquo;shit I learned
after using Hiera in anger.&rdquo; Here are a couple of the most popular questions
I hear, and my usual responses&hellip;</p>

<h2>&ldquo;How should I setup my hierarchy?&rdquo;</h2>

<p>This is such a subjective question because it&rsquo;s specific to your organization
(because it&rsquo;s your data). I usually ask back &ldquo;What are the things about your
nodes that are different, and when are they different?&rdquo; Usually I hear something
back like &ldquo;Well, nodes in this datacenter have different DNS settings&rdquo; or
&ldquo;Application servers in production use one version of java, and those in dev
use a different version&rdquo; or &ldquo;All machines in the dev environment in this datacenter
need to have a specific repository&rdquo;. All of these replies give me ideas to your
hierarchy.  When you think of Hiera as a giant conditional statment, you can
start seeing how your hierarchy could be laid out.  With the first response, we
know we need a <code>location</code> fact to determine where a node is, and then we can
have a hierarchy level for that location. The second response tells me we need
a level for the application tier (i.e. dev/test/prod).  The third response tells
me we need a level that combines both the location and the application tier. When
you add in that you should probably have a node-specific level at the top (for
overrides) and a default level at the bottom (or not: see the next section), I&rsquo;m
starting to picture this:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="l-Scalar-Plain">:hierarchy</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;nodes/%{::clientcert}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{::location}/%{::applicationtier}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{::location}/common&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;tier/%{::applicationtier}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">common</span>
</span></code></pre></td></tr></table></div></figure>


<p>Every time you have a need, you consider a level.  Now, obviously, it doesn&rsquo;t
mean that you NEED a level for every request (sometimes if it&rsquo;s an edge case
you can handle it in the profile or the role). There&rsquo;s a performance hit for
every level of your Hiera hierarchy, so ideally keep it minimal (or around
5 levels or so), but we&rsquo;re talking about flexibility here, and, if that&rsquo;s more
important than performance then you should go for it.</p>

<p>Next comes ordering. This one&rsquo;s SLIGHTLY easier &ndash; your hierarchy should read from
most-specific to least-specific. Note that when you specify an application tier
at a specific location that that it is MORE specific than just saying &ldquo;all nodes in
this application tier.&rdquo; Sometimes you will have levels that might be hard to
define an order &ndash; such as location vs. application tier. You kinda just have to
go with your gut here. In many cases you may find that the data you put in those
two levels will be entirely different (location-based data may not ever overlap
with application-tier-specific data). Do remember than any time you change the
order of your hierarchy you&rsquo;re going to introduce the possibility that values
get flip/flopped.</p>

<p>If you look at level 3 of the hierarchy above, you&rsquo;ll see that I have &lsquo;common&rsquo;
at the end. Some people like this syntax (where they put a &lsquo;common&rsquo; file in a
folder that matches the fact they&rsquo;re checking against), and some people prefer
a filename matching the fact.  Do what makes you happy, but, in this case,
we can unify the location folder and just put the common file underneath the
application tier files.</p>

<p>Finally, DO MAKE USE OF FOLDERS!  For the love of god, this. Putting all files
in a single folder both makes that a BIG folder, but also introduces a namespace
collision (i.e. what if you have a location named &lsquo;dev&rsquo; for example? Now you have
both an application tier and a location with the same name.  Oops).</p>

<p>How you setup your hierarchy is up to you, but this should hopefully give you
somewhere to start.</p>

<h2>Common.yaml, your organization&rsquo;s common values &ndash; <strong>REVISED</strong></h2>

<p><strong>UPDATE &ndash; 28 October</strong></p>

<p><em>Previously, this section was where I presented the idea of removing the lowest
level of the hierarchy as a way of ensuring that you didn&rsquo;t omit a value in Hiera
(the idea being that common values would be in the profile, anything higher would
be in Hiera, and all your &lsquo;defaults&rsquo;, or &lsquo;common values&rsquo; would be inside the profile).
The idea of removing the lowest level of the Hiera hierarchy was always something
I was kicking around in my head, but R.I. made a comment below that&rsquo;s made me revise
my thought process. There&rsquo;s still a greater concern around definitively tracking
down values pulled from Hiera, but I think we can accomplish that through other
means. I&rsquo;m going to revise what I wrote below to point out the relevant details.</em></p>

<p>When using Hiera, you need to define a hierarchy that Hiera uses in its search
for your data. Most often, it looks something like this:</p>

<figure class='code'><figcaption><span>hiera.yaml </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='yaml'><span class='line'><span class="nn">---</span>
</span><span class='line'><span class="l-Scalar-Plain">:backends</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">yaml</span>
</span><span class='line'><span class="l-Scalar-Plain">:yaml</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="l-Scalar-Plain">:datadir</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">/etc/puppetlabs/puppet/hieradata</span>
</span><span class='line'><span class="l-Scalar-Plain">:hierarchy</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;nodes/%{::clientcert}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;location/%{::location}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;environment/%{::applicationtier}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">common</span>
</span></code></pre></td></tr></table></div></figure>


<p>Notice that little &ldquo;common&rdquo; at the end?  That means that, failing everything
else, it&rsquo;s going to look in <code>common.yaml</code> for a value. I had thought of common
as the &lsquo;defaults&rsquo; level, but the reality is that it is a list of values common
across all the nodes in your infrastructure.  These are the values, SPECIFIC TO
YOUR ORGANIZATION, that should be the same everywhere. Barring an override at a
higher level, these values are your organization&rsquo;s &lsquo;defaults&rsquo;, if you will.</p>

<p>Previously, you may have heard me rail against Hiera&rsquo;s optional second argument
and how I really don&rsquo;t like it.  Take this example:</p>

<figure class='code'><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='puppet'><span class='line'><span class="nv">$foo</span> <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;port&#39;</span><span class="p">,</span> <span class="s1">&#39;80&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Given this code, Hiera is going to look for a parameter called &lsquo;port&rsquo; in its
hierarchy, and, if it doesn&rsquo;t find one in ANY of the levels, assign back a default
value of &lsquo;80&rsquo;.  I don&rsquo;t like using this second argument because:</p>

<ol>
<li>If you forget to enter the &lsquo;port&rsquo; parameter into the hierarchy, or typo it in the YAML file, Hiera will gladly assign the default value of &lsquo;80&rsquo; (which, unless you&rsquo;re checking for this, might sneak and get into production)</li>
<li>Where is the real &lsquo;default&rsquo; value: the value in <code>common.yaml</code> or the optional second argument?</li>
</ol>


<p>It actually depends on where you do the hiera() call as to what &lsquo;kind&rsquo; of
default value this is. Note that previously we talked about how the &lsquo;common&rsquo;
level represented values common across your infrastructure. If you do this
hiera() call inside a profile (which is where I recommend it be done), providing
the optional second argument ends up being redundant (i.e. the value should be
inside Hiera).</p>

<p>The moral of this story being: values common to all nodes should be in the
lowest level of the Hiera hierarchy, and all explicit hiera calls should
omit the default second argument if that common value is expected to be found
in the hierarchy.</p>

<h2>Data Bindings</h2>

<p>In Puppet 3, we introduced the concept of &lsquo;data bindings&rsquo; for parameterized classes,
which meant that Puppet now had another choice for gathering parmeter values.
Previously, the order Puppet would look to assign a value for parameters to
classes was:</p>

<ol>
<li>A value passed to the class via the parameterized class syntax</li>
<li>A default value provided by the class</li>
</ol>


<p>As of Puppet 3, this is the new parameter assignment order:</p>

<ol>
<li>A value passed to the class via the parameterized class syntax</li>
<li>A Hiera lookup for <em>classname::parametername</em></li>
<li>A default value provided by the class</li>
</ol>


<p>Data bindings is meant to be pluggable to allow for ANY data backend, but,
as of this writing, there&rsquo;s currently only one: Hiera.  Because of this,
Puppet will now automatically do a Hiera lookup for every parameter to a
parameterized class that isn&rsquo;t explicitly passed a value via the parameterized
class syntax (which means that if you just do <code>include classname</code>, Puppet
will do a Hiera lookup for EVERY parameter defined to the &ldquo;classname&rdquo; class).</p>

<p>This is really cool because it means that you can just add <em>classname::parametername</em>
to your Hiera setup, and, as long as you&rsquo;re not EXPLICITLY passing that
parameter&rsquo;s value to the class, Puppet will do a lookup and find the value.</p>

<p>It&rsquo;s also completely transparent to you unless you know it&rsquo;s happening.</p>

<p>The issue here is that this is new functionality to Puppet, and it feels like
magic to me. You can make the argument and say &ldquo;If you don&rsquo;t start using it,
Gary, people will never take to it,&rdquo; however I feel like this kind of magical
lookup in the background is always going to be a bad thing.</p>

<p>There&rsquo;s also another problem.  Consider a Hiera hierarchy that has 15 levels
(they exist, TRUST ME).  What happens if you don&rsquo;t define ANY parameters in
Hiera in the form of <em>classname::parametername</em> and simply want to rely on
the default values for every class?  Well, it means that Hiera is STILL going
to be triggered for every parameter to a class that isn&rsquo;t explicitly passed a
value.  That&rsquo;s a hell of a performance hit.  Fortunately, there&rsquo;s a way to
disable this lookup.  Simply add the following to the Puppet master&rsquo;s <code>puppet.conf</code>
file:</p>

<figure class='code'><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=''><span class='line'>data_binding_terminus = none</span></code></pre></td></tr></table></div></figure>


<p>It&rsquo;s going to be up to how your team needs to work as to whether you use Hiera
data bindings or not. If you have a savvy team that feels they can debug these
lookups, then cool &ndash; use the hell out of it. I prefer to err on the side of an
explicit hiera() lookup for every value I&rsquo;m querying, even if it&rsquo;s a lot of extra
lines of code. I prefer the visibility, especially for new members to your team.
For those people with large hierarchies, you may want to weigh the performance
hit.  Try to disable data bindings and see if your master is more performant. If
so, then explicit hiera() calls may actually buy you some rewards.</p>

<p><strong>PROS:</strong></p>

<ul>
<li>Adding parameters to Hiera in the style of <em>classname::parametername</em> will set parameterized class values automatically</li>
<li>Simplified code &ndash; simply use the include() function everywhere (which is safer than the parameterized class syntax)</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>Lookup is completely transparent unless you know what&rsquo;s going on</li>
<li>Debugging parameter values can be difficult (especially with typos or forgetting to set values in Hiera)</li>
<li>Performance hit for values you want to be assigned the class default value</li>
</ul>


<h2>Where to data &ndash; Hiera or Profile?</h2>

<p>&ldquo;Does this go right into the Profile or into Hiera?&rdquo;  I get that question
repeatedly when I&rsquo;m working with customers. It&rsquo;s a good question, and one of
the quickest ways to blow up your YAML files in Hiera. Here&rsquo;s the order I use
when deciding where to put data:</p>

<h3>WHERE did that data come from?</h3>

<p>Remember that the profile is YOUR implementation &ndash; it describes how YOU define
the implementation of a piece of technology in YOUR organization. As such, it&rsquo;s
less about Puppet code and more about pulling data and passing it TO the Puppet
code. It&rsquo;s the glue-code that grabs the data and wires it up to the model that
uses it. How it grabs the data is not really a big deal, so long as it grabs
the RIGHT data &ndash; right? You can choose to hardcode it into the Profile, or use
Hiera, or use some other magical data lookup mechanism &ndash; we don&rsquo;t really care
(so long as the Profile gathers the data and passes it to the correct Puppet
class).</p>

<p>The PROBLEM here is debugging WHERE the data came from. As I said previously,
Hiera has a level for all bits of data common to your organization, and, obviously,
data overridden at a higher level takes precedence over the &lsquo;common&rsquo; level at
the bottom. With Hiera, unless you run the <code>hiera</code> binary in debug mode (-d),
you can never be completely sure where the data came from. Puppet has no way of
dumping out every variable and where it came from (whether Hiera or set directly
in the DSL, and, if it WAS Hiera, exactly what level or file it came from).</p>

<p>It is THIS REASON that causes me to eschew things like data bindings in Puppet.
Debugging where a value came from can be a real pain in the ass. If there were
amazing tooling around this, I would 100% support using data bindings and just
setting everything inside Hiera and using the include() function, but, alas,
that&rsquo;s not been my experience. Until then, I will continue to recommend explicit
<code>hiera</code> calls for visibility into when Hiera is being called and when values
are being set inside the DSL.</p>

<h3>Enter the data into the Profile</h3>

<p>One of the first choices people make is to enter the data (like ntpserver
address, java version, or whatever it is) directly into the Profile.
&ldquo;BUT GARY! IT&rsquo;S GOING TO MAKE IT HARD TO DEBUG!&rdquo;  Not really. You&rsquo;re going to
have to open the Profile anyway to see what&rsquo;s going on (whether you pull the
data from Hiera or hardcode it in the Profile), right? And, arguably, the
Profile is legible&hellip;doing Hiera lookups gives you flexibility at a cost of
abstracting away how it got that bit of data (i.e. &ldquo;It used Hiera&rdquo;). For newer
users of Puppet, having the data in the Profile is easier to follow. So, in the
end, putting the data into the Profile itself is the least-flexible and most-visible
option&hellip;so consequently people consider it as the first available option. This option
is good for common/default values, BUT, if you eventually want to use Hiera, you need
to re-enter the data into the common level of Hiera. It also splits up your
&ldquo;source of truth&rdquo; to include BOTH the Profile manifest and Hiera. In the end,
you need to weigh your team&rsquo;s goals, who has access to the Hiera repo, and
how flexible you need to be with your data.</p>

<p><strong>PROS:</strong></p>

<ul>
<li>Data is clearly visible and legible in the profile (no need to open additional files)</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>Inability to redefine variables in Puppet DSL makes any settings constants by default (i.e. no overriding permitted)</li>
<li>Data outside of Hiera creates a second &ldquo;source of truth&rdquo;</li>
</ul>


<h3>Enter the data into Hiera</h3>

<p>If you find that you need to have different bits of data for different nodes
(i.e. a different version of Java in the dev tier instead of the prod tier),
then you can look to put the data into Hiera.  Where to put the data is going
to depend on your own needs &ndash; I&rsquo;m trusting that you can figure this part out &ndash; but
the bigger piece here is that once the data is in Hiera you need to ensure
you&rsquo;re getting the RIGHT data (i.e. if it&rsquo;s overridden at a higher level, you
are certain you entered it into the right file and didn&rsquo;t typo anything).</p>

<p>This answers that &ldquo;where&rdquo; question, but doesn&rsquo;t answer the &ldquo;what&rdquo; question&hellip;as
in &ldquo;What data should I put into Hiera?&rdquo;  For that, we have another section&hellip;</p>

<p><strong>PROS:</strong></p>

<ul>
<li>Flexibility in returning different values based on different conditions</li>
<li>All the data is inside one &lsquo;source of truth&rsquo; for data according to your organization</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>Visibility &ndash; you must do a Hiera lookup to find the value (or open Hiera&rsquo;s YAML files)</li>
</ul>


<h2>&ldquo;What exactly goes into Hiera?&rdquo;</h2>

<p>If there were one question that, if answered incorrectly, could make or break
your Puppet deployment, this would be it. The greatest strength and weakness of
Hiera is its flexibility.  You can truly put almost anything in Hiera, and, when
combined with something like the create_resources() function, you can create
your own YAML configuration language (tip: don&rsquo;t actually do this).</p>

<p>&ldquo;But, seriously, what should go into Hiera, and what shouldn&rsquo;t?&rdquo;</p>

<p>The important thing to consider here is the price you pay by putting data into
Hiera. You&rsquo;re gaining flexibility at a cost of visibility.  This means that you
can do things like enter values at all level of the hierarchy that can be
concatenated together with a single hiera_array() call, BUT, you&rsquo;re losing the
visibility of having the data right in front of you (i.e. you need to open up
all the YAML files individually, or use the <code>hiera</code> binary to debug how you got
those values). Hiera is REALLY COOL until you have to debug why it grabbed (or
DIDN&rsquo;T grab) a particular value.</p>

<p>Here&rsquo;s what I usually tell people about what should be put into Hiera:</p>

<ul>
<li>The exact data values that need to be different conditionally (i.e. a different ntp server for different sites, different java versions in dev/prod, a password hash, etc.)</li>
<li>Dynamic data expressed in multiple levels of the hierarchy (i.e. a lookup for &lsquo;packages&rsquo; that returns back an array of all the values that were found in all the levels of the hierarchy)</li>
<li>Resources as a hash ONLY WHEN ABSOLUTELY NECESSARY</li>
</ul>


<h3>Puppet manifest vs. create_resources()</h3>

<p>Bullets 1 and 2 above should be pretty straightforward &ndash; you either need to use
Hiera to grab a specific value or return back a list of ALL the values from ALL
the levels of the hierarchy. The point here is that Hiera should be returning
back only the minimal amount of data that is necessary (i.e. instead of
returning back a hash that contains the title of the resource, all the attributes
of the resource, and all the attribute values for that resource, just return
back a specific value that will be assigned to an attribute&hellip;like the password
hash itself for a user). This data lookup appears to be &ldquo;magic&rdquo; to new users of
Puppet &ndash; all they see is the magic phrase of &ldquo;hiera&rdquo; and a parameter to search
for &ndash; and so it becomes slightly confusing. It IS, however, easier to understand
that this magical phrase will return data, and that that data is going to be used
to set the value for an attribute. Consider this example:</p>

<figure class='code'><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='puppet'><span class='line'><span class="nv">$password</span> <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;garypassword&#39;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nc">user</span> <span class="p">{</span> <span class="s1">&#39;gary&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span>   <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">uid</span>      <span class="p">=&gt;</span> <span class="s1">&#39;5001&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">gid</span>      <span class="p">=&gt;</span> <span class="s1">&#39;gary&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">shell</span>    <span class="p">=&gt;</span> <span class="s1">&#39;zsh&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">password</span> <span class="p">=&gt;</span> <span class="nv">$password</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>This leads us to bullet 3, which is &ldquo;the Hiera + create_resources() solution.&rdquo;
This solution allows you to lookup data from within Hiera and pass it directly
to a function where Puppet creates the individual resources as if you had typed
them into a Puppet manifest itself. The previous example can be entered into
a Hiera YAML file like so:</p>

<figure class='code'><figcaption><span>sysadmins.yaml </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='yaml'><span class='line'><span class="l-Scalar-Plain">users</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="l-Scalar-Plain">gary</span><span class="p-Indicator">:</span>
</span><span class='line'>    <span class="l-Scalar-Plain">ensure</span><span class="p-Indicator">:</span> <span class="s">&#39;present&#39;</span>
</span><span class='line'>    <span class="l-Scalar-Plain">uid</span><span class="p-Indicator">:</span> <span class="s">&#39;5001&#39;</span>
</span><span class='line'>    <span class="l-Scalar-Plain">gid</span><span class="p-Indicator">:</span> <span class="s">&#39;gary&#39;</span>
</span><span class='line'>    <span class="l-Scalar-Plain">shell</span><span class="p-Indicator">:</span> <span class="s">&#39;zsh&#39;</span>
</span><span class='line'>    <span class="l-Scalar-Plain">password</span><span class="p-Indicator">:</span> <span class="s">&#39;biglongpasswordhash&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>And then a resource can be created inside the Puppet DSL by doing the following:</p>

<figure class='code'><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='puppet'><span class='line'><span class="nv">$users</span> <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;users&#39;</span><span class="p">)</span>
</span><span class='line'><span class="nf">create_resources</span><span class="p">(</span><span class="s1">&#39;users&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Both examples are functionally identical, except the first one only uses Hiera
to get the password hash value, whereas the second one grabs both the
attributes, and their values, for a specific resource. Imagine Puppet gives you
an error with the &lsquo;gary&rsquo; user resource and you were using the latter example.
You grep your Puppet code looking for &lsquo;gary&rsquo;, but you won&rsquo;t find that user
resource in your Puppet manifest anywhere (because it&rsquo;s being created with the create_resources() function).
You will instead have to know to go into Hiera&rsquo;s data directory, then the
correct datafile, and then look for the hash of values for the &lsquo;gary&rsquo; user.</p>

<h3>Functional differences between the two approaches</h3>

<p>Functionally, you COULD do this either way. When you come up with a solution
using create_resources(), I challenge you to draw up another solution using
Puppet code in a Puppet manifest (however lengthy it may be) that queries Hiera
for ONLY the specific values necessary. Consider this example, but, instead,
you need to manage 500 users.
If you use create_resources(), you would then need to add 500 more blocks to
the &lsquo;users&rsquo; parameter in your Hiera datafiles.  That&rsquo;s a lot of YAML. And on
what level will you add these blocks? <code>prod.yaml</code>? <code>dev.yaml</code>? Are you using a
<code>common.yaml</code>? Your YAML files suddenly got huge, and the rest of your team
modifying them will not be so happy to scroll through 500 entries. Now consider
the first example using Puppet code. Your Puppet manifest suddenly grew, but it
didn&rsquo;t affect all the OTHER manifests out there: only this file. The Hiera YAML
files will still grow &ndash; but now 500 individual lines instead of 3000 lines in
the previous example. Okay, now which one is more LEGIBLE? I would argue that
the Puppet manifest is more legible, because I consider the Puppet DSL to be
very legible (again, subject to debate versus YAML). Moreover, when debugging,
you can stay inside Puppet files more often using Puppet manifests to define
your resources. Using create_resources, you need to jump into Hiera more often.
That&rsquo;s a context shift, which adds more annoyance to debugging. Also, it
creates multiple &ldquo;sources of truth.&rdquo; Suddenly you have the ability of entering
data in Hiera as well as entering it in the Puppet manifest, which may be clear
to YOU, but if you leave the company, or you get another person on your team,
they may choose to abuse the Hiera settings without knowing why.</p>

<p>Now consider an example that you might say is more tailored to create_resources().
Say you have a defined type that sets up tomcat applications. This defined type
accepts things like a path to install the application, the application&rsquo;s package
name, the version, which tomcat installation to target, and etc. Now consider
that all application servers need application1, but only a couple
of servers need application2, and a very snowflake server needs application3 (in
this case, we&rsquo;re NOT saying that all applications are on all boxes and that their
data, like the version they&rsquo;re using, is different. We&rsquo;re actually saying that
different machines require entirely different applications).</p>

<p>Using Hiera + create_resources() you could enter the resource for the
application1 at a low level, then, at a higher level, add the resource for
application2, and finally add the resource for application3 at the
node-specific level. In the end, you can do a hiera_hash() lookup to discover
and concatenate all resources from all levels of the hierarchy and pipe that to
create_resources.</p>

<p>How would you do this with Puppet code?  Well, I would create profiles for every
application, and either different roles for the different kinds of servers (i.e.
the snowflake machine gets its own role), or conditional checks inside the role
(i.e. if this node is at the London location, it gets these application profiles,
and etc&hellip;).</p>

<p>Now which is more legible? At this point, I&rsquo;d still say that separate profiles
and conditional checks in roles (or sub-roles) are more legible &ndash; including
a class is a logical thing to follow, and conditionals inside Puppet code are
easy to follow. The create_resources() solution just becomes magic. Suddenly,
applications are on the node. If you want to know where they came from, you
have to switch contexts and open Hiera data files or use the <code>hiera</code> binary
and do a debug run. If you&rsquo;re a small team that&rsquo;s been using Puppet forever,
then rock on and go for it. If you&rsquo;re just getting started, though, I&rsquo;d shy
away.</p>

<h3>Final word on create_resources?</h3>

<figure class='code'><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=''><span class='line'>Some people, when confronted with a problem, think “I know, I'll use create_resources()."
</span><span class='line'>Now they have two problems.</span></code></pre></td></tr></table></div></figure>


<p>The create_resources() function is often called the &ldquo;PSE Swiss Army knife&rdquo;
(or, Professional Services Engineer &ndash; the people who do what
I do and consult with our customers) because we like to break it out when we&rsquo;re
painted into a corner by customer requirements. It will work ANYWHERE, but, again,
at that cost of visibility. I am okay with someone using it so long as they
understand the cost of visibility and the potential debugging issues they&rsquo;ll hit.
I will always argue against using it, however, for those reasons. More code in
a Puppet manifest is not a bad thing&hellip;especially if it&rsquo;s reasonably legible
code that can be kept to a specific class. Consider the needs and experience
level of your team before using create_resources() &ndash; if you don&rsquo;t have a good
reason for using it, simply don&rsquo;t.</p>

<h3>create_resources()</h3>

<p><strong>PROS:</strong></p>

<ul>
<li>Dynamically iterate and create resources based on Hiera data</li>
<li>Using Hiera&rsquo;s hash merging capability, you can functionally override resource values at higher levels of the hierarchy</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>Decreased visibility</li>
<li>Becomes a second &lsquo;source of truth&rsquo; to Puppet</li>
<li>Can increase confusion about WHERE to manage resources</li>
<li>When used too much, it creates a DSL to Puppet&rsquo;s DSL (DSLs all the way down)</li>
</ul>


<h3>Puppet DSL + single Hiera lookup</h3>

<p><strong>PROS:</strong></p>

<ul>
<li>More visible (sans the bit of data you&rsquo;re looking up)</li>
<li>Using wrapper classes allows for flexibility and conditional inclusion of resources/classes</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>Very explicit &ndash; doesn&rsquo;t have the dynamic overriding capability like Hiera does</li>
</ul>


<h2>Using Hiera as an ENC</h2>

<p>One of the early &ldquo;NEAT!&rdquo; moments everyone has with Hiera is using it as an
External Node Classifier, or ENC. There is a function called <code>hiera_include()</code>
that allows you to include classes into the catalog as if you were to write
&ldquo;include (classname)&rdquo; in a Puppet manifest.  It works like this:</p>

<figure class='code'><figcaption><span>london.yaml </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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="l-Scalar-Plain">classes</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">profiles::london::base</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">profiles::london::network</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>dev.yaml </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='yaml'><span class='line'><span class="l-Scalar-Plain">classes</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">profiles::tomcat::application2</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>site.pp</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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">node</span> <span class="s">default</span> <span class="p">{</span>
</span><span class='line'>  <span class="nf">hiera_include</span><span class="p">(</span><span class="s1">&#39;classes&#39;</span><span class="p">)</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>Given the above example, the hiera_include() function will search every level
of the hierarchy looking for a parameter called &lsquo;classes&rsquo;. It returns
a concatenated list of classnames, which it then passes to Puppet&rsquo;s include()
function (in the end, Puppet will declare the profiles::london::base,
profiles::london::network, and profiles::tomcat::application2 classes). Puppet
puts the contents of these classes into the catalog, and away we go. This is
awesome because you can change the classification of a node conditionally
according to a Hiera lookup, and it&rsquo;s terrible because you can CHANGE THE
CLASSIFICATION OF A NODE CONDITIONALLY ACCORDING TO A HIERA LOOKUP!  This means
that anyone with access to the repo holding your Hiera data files can affect
changes to every node in Puppet just by modifying a magical key. It also means
that in order to see the classification for a node, you need to do a Hiera
lookup (i.e. you can&rsquo;t just open a file and see it).</p>

<p>Remember that WHOLE blog post about Roles and Profiles?  I do, because I wrote
the damn thing. <a href="http://bit.ly/puppetworkflows2">You can even go back and read it again, too, if you want to.</a>
One of the core tenets of that article was that each node get classified with a
single role. If you adhere to that (and you should; it makes for a much more
logical Puppet deployment), a node really only ever needs to be classified
ONCE. You don&rsquo;t NEED this conditional classification behavior. It&rsquo;s one of those
&ldquo;It seemed like a good idea at the time&rdquo; moments that I assure you will pass.</p>

<p>Now, you CAN use Roles with hiera_include() &ndash; simply create a Facter fact that
returns the node&rsquo;s role, add a level to the Hiera hierarchy for this role fact,
and in the role&rsquo;s YAML file in Hiera, simply do:</p>

<figure class='code'><figcaption><span>appserver.yaml </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='yaml'><span class='line'><span class="l-Scalar-Plain">classes</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">role::application_server</span>
</span></code></pre></td></tr></table></div></figure>


<p>Then you can use the same hiera_include() call in the default node definition
in <code>site.pp</code>. The ONLY time I recommend this is if you don&rsquo;t already have some
other classification method. The downside of this method is that if your role
fact CHANGES, for some reason or another, classification immediately changes.
Facts are NOT secure &ndash; they can be overridden really easily. I don&rsquo;t like to
leave classification to an insecure method that anyone with root access on a
machine can change. Using an ENC or <code>site.pp</code> for classification means that the
node ABSOLUTELY CANNOT override its classification. It&rsquo;s the difference between
being authoritative and simply &lsquo;suggesting&rsquo; a classification.</p>

<p><strong>PROS:</strong></p>

<ul>
<li>Dynamic classification: no need to maintain a site.pp file or group in the Console</li>
<li>Fact-based: a node&rsquo;s classification can change immediately when its role fact does</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>Decreased visibility: need to do a Hiera lookup to determine classification</li>
<li>Insecure: since facts are insecure and can be overridden, so can classification</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Puppetconf 2014 Talk - the Refactor Dance]]></title>
    <link href="http://garylarizza.com/blog/2014/10/23/puppetconf-2014-talk/"/>
    <updated>2014-10-23T08:15:42-05:00</updated>
    <id>http://garylarizza.com/blog/2014/10/23/puppetconf-2014-talk</id>
    <content type="html"><![CDATA[<p>This year at Puppetconf 2014, I presented a 1.5 hour talk entitled &ldquo;The Refactor
Dance&rdquo; that comprised nearly EVERYTHING that I&rsquo;ve written about in my Puppet
Workflows series (from writing better component modules, to Roles/Profiles,
to Workflow, and lots of stories in-between) as well as a couple of bad words,
a pair of leather pants (trousers), and an Uber story that beats your Uber
story. It&rsquo;s long, informative, and you get to watch the sweat stains under my
arms grow in an attractive grey Puppet Labs shirt.  What&rsquo;s not to love?</p>

<p><a href="https://puppetlabs.com/presentations/workshop-doing-refactor-dance-making-your-puppet-modules-more-modular-gary-larizza">To watch the video, click here to check it out!</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[On Dependencies and Order]]></title>
    <link href="http://garylarizza.com/blog/2014/10/19/on-dependencies-and-order/"/>
    <updated>2014-10-19T08:09:53-05:00</updated>
    <id>http://garylarizza.com/blog/2014/10/19/on-dependencies-and-order</id>
    <content type="html"><![CDATA[<p>This blog post was born out of a number of conversations that I&rsquo;ve had about
Puppet, its dependency model, and why &lsquo;ordering&rsquo; is not necessarily the way to
think about dependencies when writing Puppet manifests. Like most everything on
this site, I&rsquo;m getting it down in a file so I don&rsquo;t have to repeat this all over
again the next time someone asks. Instead, I can point them to this page (and,
when they don&rsquo;t actually <strong>READ</strong> this page, I can end up explaining everything
I&rsquo;ve written here anyways&hellip;).</p>

<p>Before we go any further, let me define a couple of terms:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>dependencies     - In a nutshell, what happens when you use the metaparameters of
</span><span class='line'>                   'before', 'require', 'subscribe' or 'notify' on resources in a
</span><span class='line'>                   Puppet manifest: it's a chain of resources that are to be
</span><span class='line'>                   evaluted in a specific order every time Puppet runs. Any failure
</span><span class='line'>                   of a resource in this chain stops Puppet from evaluating the
</span><span class='line'>                   remaining resources in the chain.
</span><span class='line'>
</span><span class='line'>evaluate         - When Puppet determines the 'is' value (or current state) of a
</span><span class='line'>                   resource (i.e. for package resources, "is the package installed?")
</span><span class='line'>
</span><span class='line'>remediate        - When Puppet determines that the 'is' value (or current state of
</span><span class='line'>                   the resource) is different from the 'should' value (or the value
</span><span class='line'>                   entered into the Puppet manifest...the way the resource SHOULD
</span><span class='line'>                   end up looking on the system) and Puppet needs to make a change.
</span><span class='line'>
</span><span class='line'>declarative(ish) - When I use the word 'declarative(ish)', I mean that the order
</span><span class='line'>                   by which Puppet evaluates resources that do not contain dependencies
</span><span class='line'>                   does not have a set procedure/order. The way Puppet EVALUATES
</span><span class='line'>                   resources does not have a set procedure/order, but the order
</span><span class='line'>                   that Puppet reads/parses manifest files IS from top-to-bottom
</span><span class='line'>                   (which is why variables in Puppet manifests need to be declared
</span><span class='line'>                   before they can be used).</span></code></pre></td></tr></table></div></figure>


<h2>Why Puppet doesn&rsquo;t care about execution order (until it does)</h2>

<p>The biggest shock to the system when getting started with a declarative (ish)
configuration management tool like Puppet is understanding that Puppet describes
the end-state of the machine, and NOT the order that it&rsquo;s (Puppet) going to
take you to that state. To Puppet, the order that it chooses to affect change
in any resource (be it a file to be corrected, a package to be installed, or
any other resource type) is entirely arbitrary because resources that have no
relationship to another resource shouldn&rsquo;t CARE about the order in which they&rsquo;re
evaluated and remediated.</p>

<p>For example, imagine Puppet is going to create both <code>/etc/sudoers</code> and update
the system&rsquo;s authorized keys file to enter all the sysadmins&#8217; SSH keys. Which
one should it do first? In an imperative system like shell scripts or
a runbook-style system, you are forced to choose an order. So I ask again,
which one goes first? If you try to update the <code>sudoers</code> file in your script
first, and there&rsquo;s a problem with that update, then the script fails and the
SSH keys aren&rsquo;t installed. If you switch the order and there&rsquo;s a problem with
the SSH keys, then you can&rsquo;t <code>sudo</code> up because the <code>sudoers</code> file hasn&rsquo;t been
touched.</p>

<p>Because of this, Puppet has always taken the stance that if there are failures,
we want to get as much of the system into a working state as possible (i.e. any
resources that don&rsquo;t depend upon the failing resource are going to still be
evaluated, or &lsquo;inspected&rsquo;, and remediated, or &lsquo;changed if need be&rsquo;). There are
definitely philosophical differences here: the argument can be made that if there&rsquo;s
a failure somewhere, the system is bad and you should cast it off until you&rsquo;ve
fixed whatever the problem is (or the part of the code causing the problem). In
virtualized or &lsquo;cloud&rsquo; environments where everything is automated, this is just
fine, but in environments without complete and full automation, sometimes you
have to fix and deal with what you have. Puppet &ldquo;believes in your system&rdquo;, which
is borderline marketing-doubletalk for &ldquo;alert you of errors and give you time
to fix the damn thing and do another Puppet run without having to spin up a whole
new system.&rdquo;</p>

<p>Once you know WHY Puppet takes the stance it does, you realize that Puppet does
not give two shits about the order of resources without dependencies. If you
write perfect Puppet code, you&rsquo;re fine. But the majority of the
known-good-world does not do that. In fact, most of us write shit code. Which
was the problem&hellip;</p>

<h2>The history of Puppet&rsquo;s ordering choices</h2>

<h3>&lsquo;Random&rsquo; random order</h3>

<p>In the early days, the only resources that were guaranteed to have a consistent
order were those resources with dependencies (i.e. as I stated above, resources
that used the &lsquo;before&rsquo;, &lsquo;require&rsquo;, &lsquo;subscribe&rsquo;, or &lsquo;notify&rsquo; metaparameters to
establish an evaluation order). Every other resource was evaluted at random
every time that Puppet ran&hellip;which meant that you could run Puppet ten times
and, theoretically, resources without dependencies could be evaluated in
a different order between every Puppet run (we call this non-deterministic
ordering). This made things REALLY hard to debug.  Take the case where you had
a catalog of thousands of resources but you forgot a SINGLE dependency between
a couple of file resources. If you roll that change out to 1000 nodes, you
might have 10 or less of them fail (because Puppet chose an evaluation order
that ordered these two resources incorrectly). Imagine trying to figure out
what happened and replicate the problem. You could waste lots of time just
trying to REPLICATE the issue, even if it was a small fix like this.</p>

<p><strong>PROS</strong>:</p>

<ul>
<li>IS there a pro here?</li>
</ul>


<p><strong>CONS</strong>:</p>

<ul>
<li>Ordering could change between runs, and thus it was very hard to debug missing dependencies</li>
</ul>


<p>Philosophically, we were correct: resources that are to be evaluated in a certain
order require dependencies. Practically, we were creating more work for ourselves.</p>

<p>Incidentally, I&rsquo;d heard that Adam Jacob, who created Chef, had cited this reason
as one of the main motivators for creating Chef. I&rsquo;d heard that as a Puppet
consultant, he would run into these buried dependency errors and want to flip
tables. Even if it&rsquo;s not a true STORY, it was absolutely true for tables where
I used to work&hellip;</p>

<h3>Title-hash, &lsquo;Predictable&rsquo; random order</h3>

<p>Cut to Puppet version 2.7 where we introduced deterministic ordering with
&lsquo;title-hash&rsquo; ordering. In a nutshell, resources that didn&rsquo;t have dependencies
would still be executed in a random order, but the order Puppet chose could be
replicated (it created a SHA1 hash based on the titles of the resources without
dependencies, and ordered the hashes alphabetically). This meant that if you
tested out a catalog on a node, and then ran that same catalog on 1000 other
nodes, Puppet would choose the same order for all 1000 of the nodes. This
gave you the ability to actually TEST whether your changes would successfully
run in production. If you omitted a dependency, but Puppet managed to pick the
correct evaluation order, you STILL had a missing dependency, but you didn&rsquo;t
care about it because the code worked. The next change you made to the catalog
(by adding or removing resources), the order might change, but you would
discover and fix the dependency at that time.</p>

<p><strong>PROS</strong>:</p>

<ul>
<li>&lsquo;Predictable&rsquo; and repeatable order made testing possible</li>
</ul>


<p><strong>CONS</strong>:</p>

<ul>
<li>Easy to miss dependency omissions if Puppet chose the right order (but do you really care?)</li>
</ul>


<h3>Manifest ordering, the &lsquo;bath salts&rsquo; of ordering</h3>

<p>Title-hash ordering seemed like the best of both worlds &ndash; being opinionated about
resource dependencies but also giving sysadmins a reliable, and repeatable, way
to test evaluation order before it&rsquo;s pushed out to production.</p>

<p>Buuuuuuuuuut, y&#8217;all JUST weren&rsquo;t happy enough, were you?</p>

<p>When you move from an imperative solution like scripts to a declarative(ish)
solution like Puppet, it is absolutely a new way to think about modeling your
system. Frequently we heard that people were having issues with Puppet because
the order that resources shows up in a Puppet master WASN&rsquo;T the order that Puppet
would evaluate the resources. I just dropped a LOT of words explaining why this
isn&rsquo;t the case, but who really has the time to read up on all of this? People
were dismissing Puppet too quickly because their expectations of how the tool
worked didn&rsquo;t align with reality. The assumption, then, was to align these
expectations in the hopes that people wouldn&rsquo;t dismiss Puppet so quickly.</p>

<p><a href="http://puppetlabs.com/blog/introducing-manifest-ordered-resources">Eric Sorenson wrote a blog post on our thesis and experimentation</a>
around manifest ordering that is worth a read (and, incidentally, is shorter
than this damn post), but the short version is that we tested this theory out
and determined that Manifest Ordering would help new users to Puppet. Because
of this work, we created a feature called &lsquo;Manifest Ordering&rsquo; that stated that
resources that DID NOT HAVE DEPENDENCIES would be evaluated by Puppet in the
order that they showed up in the Puppet manifest (when read top to bottom). If
a resource truly does not have any dependencies, then you honestly should not
care one bit what order it&rsquo;s evaluated (because it doesn&rsquo;t matter).  Manifest
Ordering made ordering of resources without dependencies VERY predictable.</p>

<p>But&hellip;.</p>

<p>This doesn&rsquo;t mean I think it&rsquo;s the best thing in the world. In fact, I&rsquo;m really
wary of how I feel people will come to use Manifest Ordering. There&rsquo;s a reason
I called it the &ldquo;bath salts of ordering&rdquo; &ndash; because a little bit of it, when
used correctly, can be a lovely thing, but too much of it, used in unintended
circumstances, leads to hypothermia, paranoia, and the desire to gnaw someone
else&rsquo;s face off. We were/are giving you a way to bypass our dependency model by
using the mental-model you had with scripts, but ALSO telling you NOT to rely
on that mental-model (and instead set dependencies explicitly using metaparameters).</p>

<p>Seriously, what could go wrong?</p>

<p>Manifest Ordering is not a substitution for setting dependencies &ndash; that IS NOT
what it was created for. <strong>Puppet Labs still maintains that you should use
dependencies to order resources and NOT simply rely on Manifest Ordering as
a form of setting dependencies!</strong> Again, the problem is that you need to KNOW
this&hellip;and if Manifest Ordering allows you to keep the same imperative
&ldquo;mindset&rdquo; inside a declarative(ish) language, then eventually you&rsquo;re going to
experience pain (if not today, but possibly later when you actually try to
refactor code, or share code, or use this code on a system that ISN&rsquo;T using
Manifest Ordering). A declarative(ish) language like Puppet requires seeing
your systems according to the way their end-state will look and worrying about
WHAT the system will look like, and not necessarily HOW it will get there. Any
shortcut to understanding this process means you&rsquo;re going to miss key bits of
what makes Puppet a good tool for modeling this state.</p>

<p><strong>PROS:</strong></p>

<ul>
<li>Evaluation order of resources without dependencies is absolutely predictable</li>
</ul>


<p><strong>CONS:</strong></p>

<ul>
<li>If used as a substitution for setting dependencies, then refactoring code (moving around the order in which resources show up in a manifest) means changing the evaluation order</li>
</ul>


<h2>What should I actually take from this?</h2>

<p>Okay, here&rsquo;s a list of things you SHOULD be doing if you don&rsquo;t want to create
a problem for future-you or future-organization:</p>

<ul>
<li>Use dependency metaparameters like &lsquo;before&rsquo;, &lsquo;require&rsquo;, &lsquo;notify&rsquo;, and &lsquo;subscribe&rsquo; if resources in a catalog NEED to be evaluated in a particular order</li>
<li>Do not use Manifest Ordering as a substitute for explicitly setting dependencies (disable it if this is too tempting)</li>
<li>Use Roles and Profiles for a logical module layout (see: <a href="http://bit.ly/puppetworkflows2">http://bit.ly/puppetworkflows2</a> for information on Roles and Profiles)</li>
<li>Order individual components inside the Profile</li>
<li>Order Profiles (if necessary) inside the Role</li>
</ul>


<p>And, seriously, trust us with the explicit dependencies. It seems like a giant
pain in the ass initially, but you&rsquo;re ultimately documenting your infrastructure,
and a dependency (or, saying &lsquo;this thing MUST come before that thing&rsquo;) is a pretty
important decision. There&rsquo;s a REASON behind it &ndash; treat it with some more weight
other than having one line come before another line, ya know? The extra time
right now is absolutely going to buy you the time you spend at home with your
kids (and by &lsquo;kids&rsquo;, I mean &lsquo;XBox&rsquo;).</p>

<p>And don&rsquo;t use bath salts, folks.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[R10k + Directory Environments]]></title>
    <link href="http://garylarizza.com/blog/2014/08/31/r10k-plus-directory-environments/"/>
    <updated>2014-08-31T14:00:00-05:00</updated>
    <id>http://garylarizza.com/blog/2014/08/31/r10k-plus-directory-environments</id>
    <content type="html"><![CDATA[<p>If you&rsquo;ve read anything I&rsquo;ve posted in the past year, you know my feelings about
the word &lsquo;environments&rsquo; and about how well we tend to name things here at
Puppet Labs (and if you don&rsquo;t, <a href="http://garylarizza.com/blog/2014/03/26/random-r10k-workflow-ideas/">you can check out that post here</a>).
Since then, Puppet Labs has released a new feature called <a href="https://docs.puppetlabs.com/puppet/latest/reference/environments.html">directory
environments (click this link for further reading)</a>
that replace the older &lsquo;config file environments&rsquo; that we all used to use (i.e.
stanzas in puppet.conf).  Directory environments weren&rsquo;t without their false
starts and issues, but further releases of Puppet, and their inclusion in
Puppet Enterprise 3.3.0, have allowed more people
to ask about them.  SO, I thought I&rsquo;d do a quick writeup about them&hellip;</p>

<h2>R10k had a child: Directory Environments</h2>

<p>The Puppet platform team had a couple of problems with config file environments
in puppet.conf &ndash; namely:</p>

<ul>
<li>Entering them in puppet.conf meant that you couldn&rsquo;t use environments named &lsquo;master&rsquo;, &lsquo;main&rsquo;, or &lsquo;agent&rsquo;</li>
<li>There was no easy/reliable way to determine all the available/used Puppet environments without making assumptions (and hacky code) &ndash; especially if someone were using R10k + dynamic environments</li>
<li>Adding more environments to <code>puppet.conf</code> made managing that file something of a nightmare (<code>environments.d</code> anyone?)</li>
</ul>


<p>Combine this with the fact that <a href="http://bit.ly/puppetworkflows3">most of the Professional Services team was
rolling out R10k to create dynamic environments</a> (which meant we
were abusing <code>$environment</code> inside <code>puppet.conf</code> and creating environments&hellip;well&hellip;
dynamically and on-the-fly), and they knew something needed to be done.
Because R10k was so popular and widely deployed, an environment solution that
was a simple step-up from an R10k deployment was made the target, and directory
environments were born.</p>

<h2>How does it work?</h2>

<p>Directory environments, essentially, are born out of a folder on the Puppet master
(typically <code>$confdir/environments</code>, where <code>$confdir</code> is <code>/etc/puppetlabs/puppet</code>
in Puppet Enterprise) wherein every subfolder is a new Puppet environment. Every
subfolder contains a couple of key items:</p>

<ul>
<li>A <code>modules</code> folder containing all modules for that environment</li>
<li>A <code>manifests/site.pp</code> file containing the site.pp file for that environment</li>
<li>A new <code>environment.conf</code> file which can be used to set the <code>modulepath</code>, the <code>environment_timeout</code>, and, a new and often-requested feature, the ability to have environment-specific <code>config_version</code> settings</li>
</ul>


<p>Basically, it&rsquo;s everything that R10k ALREADY does with a couple of added goodies
dropped into an <code>environment.conf</code> file. <a href="https://docs.puppetlabs.com/puppet/latest/reference/environments_configuring.html">Feel free to read the official docs
on configuring directory environments</a> for further information
on all of the goodies!</p>

<h2>Cool, how do we set it up?</h2>

<p>It wouldn&rsquo;t be one of my blog posts if it didn&rsquo;t include exact steps to configure
shit, would it? For this walkthrough, I&rsquo;m using a Centos 6.5 vm with DNS working
(i.e. the node can ping itself and knows its own hostname and FQDN), and I&rsquo;ve
already installed an All-in-one installation of Puppet Enterprise 3.3.0. For
the walkthrough, we&rsquo;re going to setup:</p>

<ul>
<li>Directory environments based on a control repo</li>
<li>Hiera data inside a <code>hieradata</code> folder in the control repo</li>
<li>Hiera to use the per-environment hieradata folder</li>
</ul>


<p>Let&rsquo;s start to break down the components:</p>

<h3>The &lsquo;Control Repo&rsquo;?</h3>

<p>Sometime between <a href="http://bit.ly/puppetworkflows3">my initial R10k post</a> and THIS post, the Puppet Labs PS
team has come to call the repository that contains the Puppetfile and is used
to track Puppet environments on all Puppet masters the &lsquo;Control Repo&rsquo; (because
it &lsquo;Controls the creation of Puppet environments&rsquo;, ya dig?  Zack Smith and
James Sweeny are actually pretty tickled about making that name stick). For
the purpose of this demonstration, I&rsquo;m using a repository on Github:</p>

<p><a href="https://github.com/glarizza/puppet_repository">https://github.com/glarizza/puppet_repository</a></p>

<p>Everything you will need for this walkthrough is in that repository, and we
will refer to it frequently. You DO NOT need to use my repository, and it&rsquo;s
definitely going to be required that you create your OWN, but it&rsquo;s there
for reference purposes (and to give you a couple of Puppet manifests to
make setup a bit easier).</p>

<h3>Configuring the Puppet master</h3>

<p>We&rsquo;re going to first clone my control repo to <code>/tmp</code> so we can use it to
configure R10k and the Puppet master itself:</p>

<figure class='code'><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=''><span class='line'>[root@master ~]# cd /tmp
</span><span class='line'>
</span><span class='line'>[root@master /tmp]# git clone https://github.com/glarizza/puppet_repository.git
</span><span class='line'>Initialized empty Git repository in /tmp/puppet_repository/.git/
</span><span class='line'>remote: Counting objects: 164, done.
</span><span class='line'>remote: Compressing objects: 100% (134/134), done.
</span><span class='line'>remote: Total 164 (delta 54), reused 81 (delta 16)
</span><span class='line'>Receiving objects: 100% (164/164), 22.68 KiB, done.
</span><span class='line'>Resolving deltas: 100% (54/54), done.
</span><span class='line'>
</span><span class='line'>[root@master /tmp]# cd puppet_repository</span></code></pre></td></tr></table></div></figure>


<p>Great, I&rsquo;ve cloned my repo. To configure R10k, we&rsquo;re going to need to pull
down Zack Smith&rsquo;s R10k module from the forge with <code>puppet module install zack/r10k</code>
and then use <code>puppet apply</code> on a manifest in my repo with
<code>puppet apply configure_r10k.pp</code>.  <strong>DO NOTE: If you want to use YOUR Control
Repo, and NOT the one I use on Github, then you need to modify the
<code>configure_r10k.pp</code> file and replace the <code>remote</code> property with the URL to
YOUR Control Repo that&rsquo;s housed on a git repository!</strong></p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master /tmp/puppet_repository:production]# puppet module install zack/r10k
</span><span class='line'>
</span><span class='line'>Notice: Preparing to install into /etc/puppetlabs/puppet/modules ...
</span><span class='line'>Notice: Downloading from https://forgeapi.puppetlabs.com ...
</span><span class='line'>Notice: Found at least one version of puppetlabs-stdlib compatible with PE (3.3.0);
</span><span class='line'>Notice: Skipping versions which don't express PE compatibility. To install
</span><span class='line'>the most recent version of the module regardless of compatibility
</span><span class='line'>with PE, use the '--ignore-requirements' flag.
</span><span class='line'>Notice: Found at least one version of puppetlabs-inifile compatible with PE (3.3.0);
</span><span class='line'>Notice: Skipping versions which don't express PE compatibility. To install
</span><span class='line'>the most recent version of the module regardless of compatibility
</span><span class='line'>with PE, use the '--ignore-requirements' flag.
</span><span class='line'>Notice: Found at least one version of puppetlabs-vcsrepo compatible with PE (3.3.0);
</span><span class='line'>Notice: Skipping versions which don't express PE compatibility. To install
</span><span class='line'>the most recent version of the module regardless of compatibility
</span><span class='line'>with PE, use the '--ignore-requirements' flag.
</span><span class='line'>Notice: Found at least one version of puppetlabs-concat compatible with PE (3.3.0);
</span><span class='line'>Notice: Skipping versions which don't express PE compatibility. To install
</span><span class='line'>the most recent version of the module regardless of compatibility
</span><span class='line'>with PE, use the '--ignore-requirements' flag.
</span><span class='line'>Notice: Installing -- do not interrupt ...
</span><span class='line'>/etc/puppetlabs/puppet/modules
</span><span class='line'>└─┬ zack-r10k (v2.2.7)
</span><span class='line'>  ├─┬ gentoo-portage (v2.2.0)
</span><span class='line'>  │ └── puppetlabs-concat (v1.0.3) [/opt/puppet/share/puppet/modules]
</span><span class='line'>  ├── mhuffnagle-make (v0.0.2)
</span><span class='line'>  ├── puppetlabs-gcc (v0.2.0)
</span><span class='line'>  ├── puppetlabs-git (v0.2.0)
</span><span class='line'>  ├── puppetlabs-inifile (v1.1.0) [/opt/puppet/share/puppet/modules]
</span><span class='line'>  ├── puppetlabs-pe_gem (v0.0.1)
</span><span class='line'>  ├── puppetlabs-ruby (v0.2.1)
</span><span class='line'>  ├── puppetlabs-stdlib (v3.2.2) [/opt/puppet/share/puppet/modules]
</span><span class='line'>  └── puppetlabs-vcsrepo (v1.1.0)
</span><span class='line'>
</span><span class='line'>[root@master /tmp/puppet_repository:production]# puppet apply configure_r10k.pp
</span><span class='line'>
</span><span class='line'>Notice: Compiled catalog for master.puppetlabs.vm in environment production in 0.71 seconds
</span><span class='line'>Warning: The package type's allow_virtual parameter will be changing its default value from false to true in a future release. If you do not want to allow virtual packages, please explicitly set allow_virtual to false.
</span><span class='line'>   (at /opt/puppet/lib/ruby/site_ruby/1.9.1/puppet/type.rb:816:in `set_default')
</span><span class='line'>Notice: /Stage[main]/R10k::Install/Package[r10k]/ensure: created
</span><span class='line'>Notice: /Stage[main]/R10k::Install::Pe_gem/File[/usr/bin/r10k]/ensure: created
</span><span class='line'>Notice: /Stage[main]/R10k::Config/File[r10k.yaml]/ensure: defined content as '{md5}5cda58e8a01e7ff12544d30105d13a2a'
</span><span class='line'>Notice: Finished catalog run in 11.24 seconds</span></code></pre></td></tr></table></div></figure>


<p>Performing those commands will successfully setup R10k to point to my Control
Repo out on Github (and, again, if you don&rsquo;t WANT that, then you need to make
the change to the <code>remote</code> property in <code>configure_r10k.pp</code>). We next need to
configure Directory Environments in <code>puppet.conf</code> by setting two attributes:</p>

<ul>
<li><code>environmentpath</code> (Or the path to the folder containing environments)</li>
<li><code>basemodulepath</code> (Or, the set of modules that will be shared across ALL ENVIRONMENTS)</li>
</ul>


<p>I have created a Puppet manifest that will set these attributes, and this
manifest requires the <code>puppetlabs/inifile</code> module from the Puppet Forge.
Fortunately, since I&rsquo;m using Puppet Enterprise, that module is already installed.
If you&rsquo;re using open source Puppet and the module is NOT installed, feel free
to install it by running <code>puppet module install puppetlabs/inifile</code>. Once
this is done, go ahead and execute the manifest by running
<code>puppet apply configure_directory_environments.pp</code>:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master /tmp/puppet_repository:production]# puppet apply configure_directory_environments.pp
</span><span class='line'>
</span><span class='line'>Notice: Compiled catalog for master.puppetlabs.vm in environment production in 0.05 seconds
</span><span class='line'>Notice: /Stage[main]/Main/Ini_setting[Configure environmentpath]/ensure: created
</span><span class='line'>Notice: /Stage[main]/Main/Ini_setting[Configure basemodulepath]/value: value changed '/etc/puppetlabs/puppet/modules:/opt/puppet/share/puppet/modules' to '$confdir/modules:/opt/puppet/share/puppet/modules'
</span><span class='line'>Notice: Finished catalog run in 0.20 seconds</span></code></pre></td></tr></table></div></figure>


<p>The last step to configuring the Puppet master is to execute an R10k run.
We can do that by running <code>r10k deploy environment -pv</code>:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master /tmp/puppet_repository:production]# r10k deploy environment -pv
</span><span class='line'>
</span><span class='line'>[R10K::Source::Git - INFO] Determining current branches for "https://github.com/glarizza/puppet_repository.git"
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment production
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying profiles into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ntp into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying profiles into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ntp into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment webinar_env
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying profiles into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying haproxy into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ntp into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying profiles into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying haproxy into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ntp into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/webinar_env/modules
</span><span class='line'>[R10K::Task::Deployment::PurgeEnvironments - INFO] Purging stale environments from /etc/puppetlabs/puppet/environments</span></code></pre></td></tr></table></div></figure>


<p>Great!  Everything should be setup (if you&rsquo;re using my repo)!  My repository has
a production branch, which is what Puppet&rsquo;s default environment is named,
so we can test that everything works by listing out all modules in the main
production environment with the <code>puppet module list</code> command:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master /tmp/puppet_repository:production]# puppet module list
</span><span class='line'>
</span><span class='line'>Warning: Module 'puppetlabs-stdlib' (v3.2.2) fails to meet some dependencies:
</span><span class='line'>  'puppetlabs-ntp' (v3.1.2) requires 'puppetlabs-stdlib' (&gt;= 4.0.0)
</span><span class='line'>/etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>├── notifyme (???)
</span><span class='line'>├── profiles (???)
</span><span class='line'>├── puppetlabs-apache (v1.1.1)
</span><span class='line'>└── puppetlabs-ntp (v3.1.2)
</span><span class='line'>/etc/puppetlabs/puppet/modules
</span><span class='line'>├── gentoo-portage (v2.2.0)
</span><span class='line'>├── mhuffnagle-make (v0.0.2)
</span><span class='line'>├── puppetlabs-gcc (v0.2.0)
</span><span class='line'>├── puppetlabs-git (v0.2.0)
</span><span class='line'>├── puppetlabs-pe_gem (v0.0.1)
</span><span class='line'>├── puppetlabs-ruby (v0.2.1)
</span><span class='line'>├── puppetlabs-vcsrepo (v1.1.0)
</span><span class='line'>└── zack-r10k (v2.2.7)
</span><span class='line'>/opt/puppet/share/puppet/modules
</span><span class='line'>├── puppetlabs-apt (v1.5.0)
</span><span class='line'>├── puppetlabs-auth_conf (v0.2.2)
</span><span class='line'>├── puppetlabs-concat (v1.0.3)
</span><span class='line'>├── puppetlabs-firewall (v1.1.2)
</span><span class='line'>├── puppetlabs-inifile (v1.1.0)
</span><span class='line'>├── puppetlabs-java_ks (v1.2.4)
</span><span class='line'>├── puppetlabs-pe_accounts (v2.0.2-3-ge71b5a0)
</span><span class='line'>├── puppetlabs-pe_console_prune (v0.1.1-4-g293f45b)
</span><span class='line'>├── puppetlabs-pe_mcollective (v0.2.10-15-gb8343bb)
</span><span class='line'>├── puppetlabs-pe_postgresql (v1.0.4-4-g0bcffae)
</span><span class='line'>├── puppetlabs-pe_puppetdb (v1.1.1-7-g8cb11bf)
</span><span class='line'>├── puppetlabs-pe_razor (v0.2.1-1-g80acb4d)
</span><span class='line'>├── puppetlabs-pe_repo (v0.7.7-32-gfd1c97f)
</span><span class='line'>├── puppetlabs-pe_staging (v0.3.3-2-g3ed56f8)
</span><span class='line'>├── puppetlabs-postgresql (v2.5.0-pe2)
</span><span class='line'>├── puppetlabs-puppet_enterprise (v3.2.1-27-g8f61956)
</span><span class='line'>├── puppetlabs-reboot (v0.1.4)
</span><span class='line'>├── puppetlabs-request_manager (v0.1.1)
</span><span class='line'>└── puppetlabs-stdlib (v3.2.2)  invalid</span></code></pre></td></tr></table></div></figure>


<p>Notice a couple of things:</p>

<ul>
<li>First, I&rsquo;ve got some dependency issues&hellip;oh well, nothing that&rsquo;s a game-stopper</li>
<li>Second, the path to the production environment&rsquo;s module is correct at: <code>/etc/puppetlabs/puppet/environments/production/modules</code></li>
</ul>


<h3>Configuring Hiera</h3>

<p>The last dinghy to be configured on this dreamboat is Hiera. Hiera is Puppet&rsquo;s
data lookup mechanism, and is used to gather specific bits of data (such
as versions of packages, hostnames, passwords, and other business-specific
data). Explaining HOW Hiera works is beyond the scope of this article, but
configuring Hiera data on a per-environment basis IS absolutely a worthwhile
endeavor.</p>

<p>In this example, I&rsquo;m going to demonstrate coupling Hiera data with the Control
Repo for simple replication of Hiera data across environments. You COULD also
choose to put your Hiera data in a separate repository and set it up in
<code>/etc/r10k.yaml</code> as another source, but that exercise is left to the reader
<a href="http://bit.ly/puppetworkflows3b">(and if you&rsquo;re interested, I talk about it in this post).</a></p>

<p>You&rsquo;ll notice that my demonstration repository ALREADY includes Hiera data,
and so that data is automatically being replicated to all environments. By
default, Hiera&rsquo;s configuration file (<code>hiera.yaml</code>) has no YAML data directory
specified, so we&rsquo;ll need to make that change.  <a href="https://github.com/glarizza/puppet_repository/blob/production/hiera.yaml">In my demonstration control
repository, I&rsquo;ve included a sample <code>hiera.yaml</code>,</a> but let&rsquo;s take a look at
one below:</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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="c1">## /etc/puppetlabs/puppet/hiera.yaml</span>
</span><span class='line'>
</span><span class='line'><span class="nn">---</span>
</span><span class='line'><span class="l-Scalar-Plain">:backends</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">yaml</span>
</span><span class='line'><span class="l-Scalar-Plain">:hierarchy</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{clientcert}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{application_tier}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">common</span>
</span><span class='line'>
</span><span class='line'><span class="l-Scalar-Plain">:yaml</span><span class="p-Indicator">:</span>
</span><span class='line'><span class="c1"># datadir is empty here, so hiera uses its defaults:</span>
</span><span class='line'><span class="c1"># - /var/lib/hiera on *nix</span>
</span><span class='line'><span class="c1"># - %CommonAppData%\PuppetLabs\hiera\var on Windows</span>
</span><span class='line'><span class="c1"># When specifying a datadir, make sure the directory exists.</span>
</span><span class='line'>  <span class="l-Scalar-Plain">:datadir</span><span class="p-Indicator">:</span> <span class="s">&quot;/etc/puppetlabs/puppet/environments/%{environment}/hieradata&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>This hiera.yaml file specifies a hierarchy with three levels &ndash; a node-specific,
level, a level for different application tiers (like &lsquo;dev&rsquo;, &lsquo;test&rsquo;, &lsquo;prod&rsquo;, and
etc), and finally makes the change we need: mapping the data directory to each
environment&rsquo;s hieradata folder.  The path to <code>hiera.yaml</code> is Puppet&rsquo;s
configuration directory (which is <code>/etc/puppetlabs/puppet</code> for Puppet
Enterprise, or <code>/etc/puppet</code> for the open source version of Puppet), so open
the file there, make your changes, and finally you&rsquo;ll need to need to restart
the Puppet master service to have the changes picked up.</p>

<p>Next, let&rsquo;s perform a test by executing the <code>hiera</code> binary from the command
line before running puppet:</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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># hiera message environment=production</span>
</span><span class='line'><span class="l-Scalar-Plain">This node is using common data</span>
</span><span class='line'>
</span><span class='line'><span class="l-Scalar-Plain">[root@master /etc/puppetlabs/puppet/environments]# hiera message environment=webinar_env -d</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:55:44 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Hiera YAML backend starting</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:55:44 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Looking up message in YAML backend</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:55:44 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Looking for data source common</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:55:44 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Found message in common</span>
</span><span class='line'><span class="l-Scalar-Plain">This node is using common data</span>
</span><span class='line'>
</span><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># hiera message environment=bad_env -d</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:58:22 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Hiera YAML backend starting</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:58:22 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Looking up message in YAML backend</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:58:22 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Looking for data source common</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 19:58:22 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Cannot find datafile /etc/puppetlabs/puppet/environments/bad_env/hieradata/common.yaml, skipping</span>
</span><span class='line'><span class="l-Scalar-Plain">nil</span>
</span></code></pre></td></tr></table></div></figure>


<p>You can see that for the first example, I passed the environment of <code>production</code>
and did a simple lookup for a key called <code>message</code> &ndash; Hiera then returned me
the value of out that environment&rsquo;s <code>common.yaml</code> file.  Next, I did another
lookup, but added <code>-d</code> to enable debug mode (debug mode on the <code>hiera</code>
binary is REALLY handy for debugging problems with Hiera &ndash; combine it with
specifying values from the command line, and you can pretty quickly simulate
what value a node is going to get).  Notice the last example where I specified
an invalid environment &ndash; Hiera logged that it couldn&rsquo;t find the datafile
requested and ultimately returned a nil, or empty, value.</p>

<p>Since we&rsquo;re working on the Puppet master machine, we can even check for a value
using <code>puppet apply</code> combined with the notice function:</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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># puppet apply -e &quot;notice(hiera(&#39;message&#39;))&quot;</span>
</span><span class='line'><span class="l-Scalar-Plain">Notice</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Scope(Class[main])</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">This node is using common data</span>
</span><span class='line'><span class="l-Scalar-Plain">Notice</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Compiled catalog for master.puppetlabs.vm in environment production in 0.09 seconds</span>
</span><span class='line'><span class="l-Scalar-Plain">Notice</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Finished catalog run in 0.19 seconds</span>
</span></code></pre></td></tr></table></div></figure>


<p>Great, it&rsquo;s working, but let&rsquo;s look at pulling data from a higher level in the
hierarchy &ndash; like from the <code>application_tier</code> level. We haven&rsquo;t defined an
<code>application_tier</code> fact, however, so we&rsquo;ll need to fake it. First, let&rsquo;s do
that with the <code>hiera</code> binary:</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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># hiera message environment=production application_tier=dev -d</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 20:04:12 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Hiera YAML backend starting</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 20:04:12 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Looking up message in YAML backend</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 20:04:12 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Looking for data source dev</span>
</span><span class='line'><span class="l-Scalar-Plain">DEBUG</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">2014-08-31 20:04:12 +0000</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Found message in dev</span>
</span><span class='line'><span class="l-Scalar-Plain">You are in the development application tier</span>
</span></code></pre></td></tr></table></div></figure>


<p>And then also with <code>puppet apply</code>:</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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># FACTER_application_tier=dev puppet apply -e &quot;notice(hiera(&#39;message&#39;))&quot;</span>
</span><span class='line'><span class="l-Scalar-Plain">Notice</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Scope(Class[main])</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">You are in the development application tier</span>
</span><span class='line'><span class="l-Scalar-Plain">Notice</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Compiled catalog for master.puppetlabs.vm in environment production in 0.09 seconds</span>
</span><span class='line'><span class="l-Scalar-Plain">Notice</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Finished catalog run in 0.18 seconds</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Tuning <code>environment.conf</code></h2>

<p>The brand-new, per-environment  <code>environment.conf</code> file is meant to be (for
the most part) a one-stop-shop for your Puppet environment tuning needs. Right
now, the only things you&rsquo;ll need to tune will be the <code>modulepath</code>,
<code>config_version</code>, and possibly the <code>environment_timeout</code>.</p>

<h3>Module path</h3>

<p>Before directory environments, every environment had its own <code>modulepath</code> that
needed to be tuned to allow for modules that were to be used by this
machine/environment, as well as shared modules.  That <code>modulepath</code> worked like
<code>$PATH</code> in that it was a priority-based lookup for modules (i.e. the first
directory in <code>modulepath</code> that had a module matching the module name you wanted
won).  It also previously required the FULL path to be used for every path in
<code>modulepath</code>.</p>

<p>Those days are over.</p>

<p>As I mentioned before, the main <code>puppet.conf</code> configuration file has a new
parameter called <code>basemodulepath</code> that can be used to specify modules that are
to be shared across ALL modules in ALL environments. Paths defined here
(typically <code>$confdir/modules</code> and <code>/opt/puppet/share/puppet/modules</code>) are
usually put at the END of a <code>modulepath</code> so Puppet can search for any
overridden modules that show up in earlier <code>modulepath</code> paths. In the previous
configuration steps, we executed a manifest that setup <code>basemodulepath</code> to
look like:</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='yaml'><span class='line'><span class="l-Scalar-Plain">basemodulepath = $confdir/modules:/opt/puppet/share/puppet/modules</span>
</span></code></pre></td></tr></table></div></figure>


<p>Again, feel free to add or remove paths (except don&rsquo;t remove
<code>/opt/puppet/share/puppet/modules</code> if you&rsquo;re using Puppet Enterprise, because
that&rsquo;s where all Puppet Enterprise modules are located), especially if you&rsquo;re
using a giant monolithic repo of modules (which was typically done before things
like R10k evolved).</p>

<p>With <code>basemodulepath</code> configured, it&rsquo;s now time to configure the <code>modulepath</code>
to be defined for every environment. My demonstration control repo contains
a sample <code>environment.conf</code> that defines a <code>modulepath</code> like so:</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='yaml'><span class='line'><span class="l-Scalar-Plain">modulepath = modules:$basemodulepath</span>
</span></code></pre></td></tr></table></div></figure>


<p>You&rsquo;ll notice, now, that there are relative paths in <code>modulepath</code>. This is
possible because now each environment contains an <code>environment.conf</code>, and thus
relative paths make sense. In this example, nodes in the production environment
(<code>/etc/puppetlabs/puppet/environments/production</code>) will look for a module by its
name FIRST by looking in a folder called <code>modules</code> inside the current
environment folder (i.e. <code>/etc/puppetlabs/puppet/environments/production/modules/&lt;module_name&gt;</code>).
If the module wasn&rsquo;t found there, it looks for the module in the order that
paths are defined for <code>basemodulepath</code> above. If Puppet fails to find a module
in ANY of the paths, a compile error is raised.</p>

<h3>Per-environment <code>config_version</code></h3>

<p><a href="https://docs.puppetlabs.com/references/stable/configuration.html#configversion">Setting <code>config_version</code> has been around for awhile</a> &ndash; hell,
I remember video of Jeff McCune talking about it at the first Puppetcamp Europe
in like 2010 &ndash; but the new directory environments implementation has fine
tuned it a bit. Previously, <code>config_version</code> was a command executed on the
Puppet master at compile time to determine a string used for versioning the
configuration enforced during that Puppet run. When it&rsquo;s not set it defaults
to something of a time/date stamp off the parser, but it&rsquo;s way more useful to
make it do something like determine the most recent commit hash from a repository.</p>

<p>In the past when we used a giant monolithic repository containing all Puppet
modules, it was SUPER easy to get a single commit hash and be done. As everyone
moved their modules into individual repositories, determining <em>WHAT</em> you were
enforcing became harder. With the birth of R10k an the control repo, we
suddenly had something we could query for the state of our modules being
enforced. The problem existed, though, that with multiple dynamic environments
using multiple git branches, <code>config_version</code> wasn&rsquo;t easily tuned to be able
to grab the most recent commit from every branch.</p>

<p>Now that <code>config_version</code> is set in a per-environment <code>environment.conf</code>, we
can make <code>config_version</code> much smarter. Again, looking in the <code>environment.conf</code>
defined in my demonstration control repo produces this:</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='yaml'><span class='line'><span class="l-Scalar-Plain">config_version = &#39;/usr/bin/git --git-dir $confdir/environments/$environment/.git rev-parse HEAD&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>This setting will cause the Puppet master to produce the most recent commit ID
for whatever environment you&rsquo;re in and embed it in the catalog and the report
that is sent back to the Puppet master after a Puppet run.</p>

<p><a href="https://tickets.puppetlabs.com/browse/PUP-3150">I actually discovered a bug in <code>config_version</code> while writing this post</a>,
and it&rsquo;s that <code>config_version</code> is subject to the relative pathing fun that other
<code>environment.conf</code> settings are subject to. Relative pathing is great for things like
<code>modulepath</code>, and it&rsquo;s even good for <code>config_version</code> if you&rsquo;re including the
script you want to run to gather the <code>config_version</code> string inside the control
repo, but using a one-line command that tries to execute a binary on the system
that DOESN&rsquo;T include the full path to the binary causes an error (because Puppet
attempts to look for that binary in the current environment path, and NOT by
searching <code>$PATH</code> on the system).  Feel free to follow or comment on the bug
if the mood hits you.</p>

<h3>Caching and environment_timeout</h3>

<p>The Puppet master loads environments on-request, but it also caches data associated
with each environment to make things faster. This caching is finally tunable on a
per-environment basis by defining the <code>environment_timeout</code> setting in
<code>environment.conf</code>.  The default setting is 3 minutes, which means the Puppet master
will invalidate its caches and reload environment data every 3 minutes, but that&rsquo;s
now tunable. <a href="https://docs.puppetlabs.com/puppet/3.6/reference/environments_configuring.html#environmenttimeout">Definitely read up on this setting before making changes.</a></p>

<h2>Classification</h2>

<p>One of the last new features of directory environments is the ability to include
an environment-specific <code>site.pp</code> file for classification. You could ALWAYS do
this by modifying the <code>manifest</code> configuration item in <code>puppet.conf</code>, but now
each environment can have its own <code>manifest</code> setting. The default behavior is
to have the Puppet master look for <code>manifests/site.pp</code> in every environment
directory, and I really wouldn&rsquo;t change that unless you have a good reason. DO
NOTE, however, that if you&rsquo;re using Puppet Enterprise, you&rsquo;ll need to be careful
with your <code>site.pp</code> file.  Puppet Enterprise defines things like the Filebucket
and overrides for the File resource in <code>site.pp</code>, so if you&rsquo;re using Puppet Enterprise,
you&rsquo;ll need to copy those changes into the <code>site.pp</code> file you add into your control
repo (as I did).</p>

<p>It may take you a couple of times to change your thinking from looking at the main
<code>site.pp</code> in <code>$confdir/manifests</code> to looking at each environment-specific <code>site.pp</code>
file, but definitely take advantage of Puppet&rsquo;s commandline tool to help you track
which <code>site.pp</code> Puppet is monitoring:</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='yaml'><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># puppet config print manifest</span>
</span><span class='line'><span class="l-Scalar-Plain">/etc/puppetlabs/puppet/environments/production/manifests</span>
</span><span class='line'>
</span><span class='line'><span class="l-Scalar-Plain">[root@master /etc/puppetlabs/puppet/environments]# puppet config print manifest --environment webinar_env</span>
</span><span class='line'><span class="l-Scalar-Plain">/etc/puppetlabs/puppet/environments/webinar_env/manifests</span>
</span></code></pre></td></tr></table></div></figure>


<p>You can see that <code>puppet config print</code> can be used to get the path to the
directory that contains <code>site.pp</code>.  Even cooler is what happens when you
specify an environment that doesn&rsquo;t exist:</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='yaml'><span class='line'><span class="p-Indicator">[</span><span class="nv">root@master /etc/puppetlabs/puppet/environments</span><span class="p-Indicator">]</span><span class="c1"># puppet config print manifest --environment bad_env</span>
</span><span class='line'><span class="l-Scalar-Plain">no_manifest</span>
</span></code></pre></td></tr></table></div></figure>


<p>Yep, Puppet tells you if it can&rsquo;t find the manifest file.  That&rsquo;s pretty cool.</p>

<h2>Wrapping Up</h2>

<p>Even though the new implementation of directory environments is meant to map
closely to a workflow most of us have been using (if you&rsquo;ve been using R10k, that is),
there are still some new features that may take you by surprise. Hopefully this
post gets you started with just enough information to setup your own test
environment and start playing. PLEASE DO make sure to file bugs on any behavior
that comes as unexpected or stops you from using your existing workflow. Cheers!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[On R10k and 'Environments']]></title>
    <link href="http://garylarizza.com/blog/2014/03/26/random-r10k-workflow-ideas/"/>
    <updated>2014-03-26T03:00:00-05:00</updated>
    <id>http://garylarizza.com/blog/2014/03/26/random-r10k-workflow-ideas</id>
    <content type="html"><![CDATA[<p>There have been more than a couple of moments where I&rsquo;m on-site with a customer
who asks a seemingly simple question and I&rsquo;ve gone &ldquo;Oh shit; that&rsquo;s a great
question and I&rsquo;ve never thought of that&hellip;&rdquo;  Usually that&rsquo;s followed by me
changing up the workflow and immediately regretting things I&rsquo;ve done on prior
gigs. Some people call that &lsquo;agile&rsquo;; I call it &lsquo;me not having the
forethought to consider conditions properly&rsquo;.</p>

<h2>&lsquo;Environment&rsquo;, like &lsquo;scaling&rsquo;, &lsquo;agent&rsquo;, and &lsquo;test&rsquo;, has many meanings</h2>

<p>It&rsquo;s not a secret that we&rsquo;ve made some shitty decisions in the past with regard
to naming things in Puppet (and anyone who asks me what <code>puppet agent -t</code>
stands for usually gets a heavy sigh, a shaken head, and an explanation emitted
in dulcet, apologetic tones). It&rsquo;s also very easy to conflate certain concepts
that unfortunately share very common labels (quick &ndash; what&rsquo;s the difference
between properties and parameters, and give me the lowdown on MCollective
agents versus Puppet agents!).</p>

<p>And then we have &lsquo;environments&rsquo; + Hiera + R10k.</p>

<h3>Puppet &lsquo;environments&rsquo;</h3>

<p>Puppet has the concept of &lsquo;environments&rsquo;, which, to me, exist to provide a
means of compiling a catalog using different paths to Puppet modules on the
Puppet master. Using a Puppet environment is the same as saying &ldquo;I made some
changes to my tomcat class, but I don&rsquo;t want to push it DIRECTLY to my production
machines yet because I don&rsquo;t drink Dos Equis. It would be great if I could stick
this code somewhere and have a couple of my nodes test how it works before
merging it in!&rdquo;</p>

<p>Puppet environments suffer some &lsquo;seepage&rsquo; issues,
<a href="http://projects.puppetlabs.com/issues/12173">which you can read about here,</a> but do a reasonable job of quickly
testing out changes you&rsquo;ve made to the Puppet DSL (as opposed to custom
plugins, as detailed in the bug). Puppet environments work well when you
need a pipeline for testing your Puppet code (again, when you&rsquo;re refactoring
or adding new functionality), and using them for that purpose is great.</p>

<h3>Internal &lsquo;environments&rsquo;</h3>

<p>What I consider &lsquo;internal environments&rsquo; have a couple of names &ndash; sometimes
they&rsquo;re referred to as application or deployment gateways, sometimes as &lsquo;tiers&rsquo;, but
in general they&rsquo;re long-term groupings that machines/nodes are attached to
(usually for the purpose of phased-out application deployments). They
frequently have names such as &lsquo;dev&rsquo;, &lsquo;test&rsquo;, &lsquo;prod&rsquo;, &lsquo;qa&rsquo;, &lsquo;uat&rsquo;, and the
like.</p>

<p>For the purpose of distinguishing them from Puppet environments, I&rsquo;m going to
refer to them as &lsquo;application tiers&rsquo; or just &lsquo;tiers&rsquo; because, fuck it, it&rsquo;s a
word.</p>

<h3>Making both of them work</h3>

<p>The problems with having Puppet environments and application tiers are:</p>

<ul>
<li>Puppet environments are usually assigned to a node for short periods of time,
while application tiers are usually assigned to a node for the life of the node.</li>
<li>Application tiers usually need different bits of data (i.e. NTP server
addresses, versions of packages, etc), while Puppet environments usually
use/involve differences to the Puppet DSL.</li>
<li>Similarly to the first point, the goal of Puppet environments is to eventually
merge code differences into the main production Puppet environment. Application
tiers, however, may always have differences about them and never become unified.</li>
</ul>


<p>You can see where this would be problematic &ndash; especially when you might want to
do things like use different Hiera values between different application tiers,
but you want to TEST out those values before applying them to all nodes in an
application tier. If you previously didn&rsquo;t have a way to separate Puppet
environments from application tiers, and you used R10k to generate Puppet
environments, you would have things like long-term branches in your repositories
that would make it difficult/annoying to manage.</p>

<p><strong>NOTE: This is all assuming you&rsquo;re managing component modules, Hiera data,
and Puppet environments using R10k.</strong></p>

<p>The first step in making both monikers work together is to have two separate
variables in Puppet &ndash; namely <code>$environment</code> for Puppet environments, and
something ELSE (say, <code>$tier</code>) for the application tier. The &ldquo;something else&rdquo; is
going to depend on how your workflow works. For example, do you have something
centrally that can correlate nodes to the tier in which they belong? If so, you
can write a custom fact that will query that service. If you don&rsquo;t have this
magical service, you can always just attach an application tier to a node in
your classification service (i.e. the Puppet Enterprise Console or Foreman).
Failing both of those, <a href="http://docs.puppetlabs.com/guides/custom_facts.html#external-facts">you can look to external facts.</a> External Fact
support was introduced into Facter 1.7 (but Puppet Enterprise has supported
them through the standard lib for quite awhile). External facts give you the
ability to create a text file inside the facts.d directory in the format of:</p>

<figure class='code'><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=''><span class='line'>tier=qa
</span><span class='line'>location=portland</span></code></pre></td></tr></table></div></figure>


<p>Facter will read this text file and store the values as facts for a Puppet run,
so <code>$tier</code> will be <code>qa</code> and <code>$location</code> will be <code>portland</code>. This is handy for
when you have arbitrary information that can&rsquo;t be easily discovered by the
node, but DOES need to be assigned for the node on a reasonably consistent
basis.  Usually these files are created during the provisioning process, but
can also be managed by Puppet.  At any rate, having <code>$environment</code> and <code>$tier</code>
available allow us to start to make decisions based on the values.</p>

<h3>Branch with $environment, Hiera with $tier</h3>

<p>Like we said above, Puppet environments are frequently short-term assignments,
while application tiers are usually long-term residencies. Relating those back
to the R10k workflow: branches to the main puppet repo (containing the
<code>Puppetfile</code>) are usually short-lived, while data in Hiera is usually
longer-lived. It would then make sense that the name of the branches to the
main puppet repo would resolve to being <code>$environment</code> (and thus the Puppet
environment name), and <code>$tier</code> (and thus the application tier) would be used
in the Hiera hierarchy for lookups of values that would remain different across
application tiers (like package versions, credentials, and etc&hellip;).</p>

<p>Wins:</p>

<ul>
<li>Puppet environment names (like repository branch names) become relatively
meaningless and are the &ldquo;means&rdquo; to the end of getting Puppet code merged into
the PUPPET CODE&rsquo;s production branch (i.e. code that has been tested to work
across all application tiers)</li>
<li>Puppet environments become short lived and thus have less opportunity to
deviate from the main production codebase</li>
<li>Differences across application tiers are locked in one place (Hiera)</li>
<li>Differences to Puppet DSL code (i.e. in Manifests) can be pushed up to the
profile level, and you have a fact (<code>$tier</code>) to catch those differences.</li>
</ul>


<p>The ultimate reason why I&rsquo;m writing about this is because I&rsquo;ve seen people try
to incorporate both the Puppet environment and application tier into both the
environment name and/or the Hiera hierarchy. Many times, they run into all
kinds of unscalable issues (large hierarchies, many Puppet environments,
confusing testing paths to &lsquo;production&rsquo;). I tend to prefer this workflow
choice, but, like everything I write about, take it and model it toward what
works for you (because what works now may not work 6 months from now).</p>

<h2>Thoughts?</h2>

<p>Like I said before, I tend to discover new corner cases that change my mind
on things like this, so it&rsquo;s quite possible that this theory isn&rsquo;t the most
solid in the world. It HAS helped out some customers to clean up their code
and make for a cleaner pipeline, though, and that&rsquo;s always a good thing. Feel
free to comment below &ndash; I look forward to making the process better for all!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[When Yellow Ruled the World]]></title>
    <link href="http://garylarizza.com/blog/2014/03/11/when-yellow-ruled-the-world/"/>
    <updated>2014-03-11T20:04:55-05:00</updated>
    <id>http://garylarizza.com/blog/2014/03/11/when-yellow-ruled-the-world</id>
    <content type="html"><![CDATA[<p><em>&ldquo;The universe (including ourselves) is made up of energy &ndash; it&rsquo;s important to
keep it positive! Be as wonderful as the world you want to live in!&rdquo;</em></p>

<p>Writing is theraputic; it&rsquo;s usually the frenetic transcription of wild thoughts
that come to me when I run, walk, or sit slave to the United flight of the
week. This piece, however, is quite possibly the hardest and most cathartic
thing I&rsquo;ve ever had to compose. I&rsquo;m writing it not necessarily to inform,
entertain, or to pursuade, but instead with the same desperation of someone
whom holds the rain in one hand and sand in the other. I&rsquo;m writing this
so I don&rsquo;t forget the only thing I have left of someone I&rsquo;d consider the best
friend I ever had: my memories of her.</p>

<p>The thing is &ndash; I&rsquo;ve had this article on my laptop for over a week now (note:
I started writing this in March; the reality is that it&rsquo;s been MONTHS), and
I can&rsquo;t seem to get it right. I&rsquo;ve written INCREDIBLY LONG tomes with crazy
amounts of detail, but it never feels &lsquo;done&rsquo; or &lsquo;right&rsquo;. I really want to give
some insight into the Tanny that <em>I</em> knew, but, at the end of the day, this
article is really for me&hellip;because I know she&rsquo;s quite comfortable with how she
lived her life, and I&rsquo;m still all over the map with mine :)</p>

<h2>Tanny from the block</h2>

<p>In my school age years, I was lucky enough to wake up AFTER the shitshow that
occurs when two teenage girls share a bathroom. Being the youngest in a family
of two older sisters, I was (mostly) shielded from the horrors born out of the
hormonal laments of middle school girls (but I DID have to get up between the
hours of Picture Pages and half-past my mother&rsquo;s call of &ldquo;GIRLS! YOU&rsquo;RE GONNA
BE LATE FOR SCHOOL!&rdquo;). The only reasonably prompt event of those mornings was the
arrival of the skinny girl with glasses who walked over from &ldquo;the back street&rdquo;,
Tanny Vonthron. Tanny (whose real name was &ldquo;Satanta, which was no name <em><em>I</em></em>
had ever seen on a keychain) usually meant one thing &ndash; it was time for my
sisters and Tanny to leave for school, and for the house to quiet-the-hell-down
just before Mr. Wizard (and, looking back, that could be what first drew me to
her).</p>

<p>Tanny was usually dressed in bright colors (come on, this WAS the 80s), sported
giant glasses, and had bangs and blonde hair for days (if &lsquo;a day&rsquo; starts at the
top of your head and ends at your shoulders). To say that she was a casualty of
the 80s would imply that ANYONE survived unscathed, but let&rsquo;s just say that
even Tim Gunn would have had extreme difficulty trying to &ldquo;make it work.&rdquo; Tanny
always a giant, friendly smile on her face that reeked of never having
a detention &ndash; if there was ever a stereotype of the smart, smiley
super-student, that was Tanny.</p>

<p>I had seen Tanny thousands of times between childhood and my
20s, but never really talked to her much (most likely because the moments we
shared together involved me wearing Ninja Turtle pajamas and her holding her
hippy-lunch waiting for the two hot messes I call sisters). In fact, the first
time I remember sharing space with her OUTSIDE of my mother&rsquo;s berber carpeting
was around the year 2000 when I started helping out with technology at the
school district I originally attended.</p>

<h2>I attempt a career</h2>

<p><strong>NOTE: I&rsquo;m totally including a ton of detail because, like I said, I don&rsquo;t
want to forget any of this. If you&rsquo;re interested in the part where I talk about
what Tanny meant to me and the last few years, be prepared to skip a bunch of
paragraphs :)</strong></p>

<p>Back then, Tanny was teaching Special Education to elementary school children,
and I was doing my best to fix the sad Mac faces of Apple 5260/120s (as
a college student who had come back to be an &ldquo;intern&rdquo;, of sorts, for the school
district I once attended). We made casual pleasantries as I updated her Special
Education reporting tool database, which is to say I said &ldquo;Hi, I need to update
your Special Education reporting tool database&rdquo; and did so in silence. When
I was done, I left with a nod, and that was about it. Elementary school
classrooms were always very weirdly-happy to me, and Tanny&rsquo;s was definitely the
epitome of the &ldquo;happy&rdquo; component. As long as I&rsquo;ve been aware of Tanny, her logo
has been the giant yellow smiley face. She has smiley face <em>EVERYTHING</em>. The
car she drives?  That would be a yellow year-2000 VW bug with the license plate
&ldquo;TANSBUG&rdquo;. Hopefully you&rsquo;re seeing the picture I&rsquo;m painting</p>

<p>Sometime around 2002, Tanny left Huron City Schools, and Ohio altogether, for
sunny North Carolina while I stayed in town and tried to wrap my head around
a TCP/IP network stack. Cut to the summer of 2005, and I&rsquo;m working in a back
office of the high school library when the principal waltzes in with Tanny in
tow. I remember flipping out with nerdy exuberance &ndash; not necessarily because it
was TANNY, but because I remembered that Tanny&rsquo;s personal computer was
a clamshell iBook, and I had just gotten in a shipment of new G5 Powermacs
(Look, for everyone who knows me NOW, you&rsquo;ve got to understand that the product
of &ldquo;me&rdquo; now is the result of years of learning social queues. I was 25, living
in the town I grew up in, working at the school I attended, and the only thing
that made me happy was working with computers.  Trust me; it was a bit tragic).
For SOME reason, I thought that this fresh stock of awesome Macs would be
a HUGE selling point in getting Tanny to work for the school district again (I
also thought that fedoras were cool back then &ndash; leave me the hell alone).
Later that day, I sang Tanny&rsquo;s praises to the principal and gave him something
like a 4-minute diatribe as to why he should absolutely hire Tanny this
instant.  It was totally unnecessary, and didn&rsquo;t sway him in the least, because
he already had a small mountain of recommendations from anyone who had ever
crossed Tanny&rsquo;s path (you&rsquo;d be a moron to not recognize how awesome Tanny was).
In the fall of 2005, I was freaking out trying to prepare for the beginning of
yet another school year (being the Director of Technology for the district at
this point), and Tanny started working for Huron City Schools again as
a special education teacher.</p>

<h2>It wasn&rsquo;t Stockholm, but we had the syndrome</h2>

<p>The Tanny Vonthron that I had &ldquo;known&rdquo; when I was younger and the Tanny Vonthron
of 2005 shared a couple of traits &ndash; they were both incredibly skinny, smiley, happy,
blonde, and positive individuals who always knew what to say, but the Tanny of
2005 had this quick, sarcastic wit about her. The measure of how much I&rsquo;m going
to like a person is usually whether or not they can keep up with the onslaught of
references I pull off the top of my head. Tanny ALWAYS caught every stupid
reference I threw and usually followed it up with a &ldquo;Nice!&rdquo; and another relevant
and even-funnier reference of her own. If you talked with her for any length of
time, you&rsquo;d get sucked into her shorthand &ldquo;language&rdquo; comprised of these references,
nicknames, and pseudonyms that were necessary when sharing drama in such a small
town where everyone is related and everyone was LISTENING (in later years, this
shorthand was also borne out of iPhone autocorrects that you would just run with &ndash; so
even when you saw &ldquo;Lola&rdquo;, &ldquo;Le Faceboom&rdquo;, &ldquo;GOL&rdquo;, &ldquo;gorp&rdquo; and whatever ducking phrase
the iPhone decided to mangle this week, you learned to be like Cover Girl &ndash; or
easy, breezy, and beautiful). Tanny knew the power of the inside joke, and
reveled in treating every verbal quip as a well-crafted riddle. She had the
&ldquo;elephant brain&rdquo; of the group, and thus could remember minutae. I loved to pride
myself on my ability to remember stupid obscure details, and we bonded almost
immediately.</p>

<p>It&rsquo;s true to say that you develop &ldquo;favorites&rdquo; wherever you work. For me, I
enjoyed the high school because the kids didn&rsquo;t cry as much when you openly
mocked them (and, being a former fat kid, I wielded a sharp stick of sarcasm
that I tended to jab with the most wanton of disregard). My sister Andrea
had come to work for the same school district as la profesora de Español,
which made things a bit more comfortable, and we had come to enjoy the snarky
sentiments of Brooke Kukay/Lorenz/Kukay &ndash; the senior english teacher who
wore black to match her cold, dead heart. We each shared a common love:
finding humor in the mundane, alcohol, and sushi (okay, maybe just the latter two,
but who&rsquo;s counting).</p>

<p>Like I said, Tanny was a high school intervention specialist
who dealt with special education children and helped them to adjust to HS work.
Throughout the day she would encounter children with amazing &ldquo;quirks&rdquo; that
others perceived as flaws. Some wanted things a particular way, others refused
to be touched, some had no concept of sarcasm, and many had issues reading
and reacting to emotions of others (while showing limited emotions themselves).
The coolest thing was, not only did Tanny consider these facets of every child&rsquo;s
personality, but she taught me to identify with them instead of judge them.
In reality, looking back on it, she was actually shining a light on certain
&ldquo;quirks&rdquo; in my own personality that I had never taken the time to face. She
was MASTERFUL at modeling correct behavior and being completely unflappable.
I was amazed that she could be completely drained yet put on the happy face
for some unhappy parent, disarm them with a smile, guide them to the answers
they needed to hear, and have them leaving like they had accomplished something.
I remember actually repeating phrases that she had used to myself to try
and remember them in case I was ever in the same situation. Here she was modeling
behaviors and teaching me how to become a better person, while simply trying
to escape an awkward situation of her own.</p>

<p>As for unflappable &ndash; that was Tanny&rsquo;s middle name. I have never seen her recoil
in the face of anything. You know the stories of flight attendents, soldiers,
and nurses who react as calm as Hindu cows in the face of trauma? That was
Tanny. The problem was &ndash; I&rsquo;m the kind of person that kicks sand on the line
you draw just to find your boundaries. It became a game to me to see how
casually I could drop the most candid, cringeworthy statements just to see if
Tanny would react. She never did. Ever. I&rsquo;m sure by now people will have posted
countless number of Tanny quotes and nuggets of information she shared with
all of us, but there was one key mantra that, <em>TO ME</em> defined Tanny: &ldquo;Never
let the bitches see you sweat.&rdquo; Now, I don&rsquo;t know that those were her actual
WORDS, but she was the complete embodiment of that phrase. Tanny never let
anyone have the power to break her. Ever. She wouldn&rsquo;t give anyone the benefit
of letting them see her shaken. To Tanny, losing yourself was losing the situation,
and that bitch never met a situation she didn&rsquo;t win.</p>

<p>Finally, Tanny was private to a fault. She bought a tiny little house in the
center of town and I had the opportunity to visit it a sum total of twice when
I knew her. Being one of those people who loved doing the &ldquo;drop in&rdquo; on people at
home, I couldn&rsquo;t understand her fear of having people visit. But it wasn&rsquo;t &ldquo;fear&rdquo;,
per se, but mainly the concept of having your own space where you could retreat
and recharge. Where Tanny and I most strongly bonded was the fact that many
people would consider the both of us as extroverted, social creatures, but, in
reality, we not only favored being alone and to ourselves, but sometimes downright
DESPISED having to leave our four walls and be social. We would lament required
attendance at events and try to plan our getaways in advance. We had systems for
signaling the other to give a &ldquo;rescue call&rdquo;, where you telephoned to try and
save someone from an event/meeting/social interaction they were required to
have. At the end of the day, we wanted that &ldquo;Tanny Time&rdquo;, as we called it,
which was that time at home doing nothing but enjoying the company of one&rsquo;s
self.</p>

<h2>Rise of the basic bitches and putting the &lsquo;pal&rsquo; in Principal</h2>

<p>I don&rsquo;t remember when it happened, I can&rsquo;t even point to exactly HOW it happened,
but Tanny, Brooke, and I became close. It could have been the fact that we were all
single in a town with an age gap between the ages of 18 &ndash; 36, it might have been
the fact that we put on a pleasant face and bit with sarcasm when you turned your
back, or it could have been a simple result of friendship by proximity, but
slowly we found ourselves communicating pretty frequently about the craziness of
the day (from stupid things like who&rsquo;s doing a reply-all to a news post, to
more important decisions like when Nagoya was going to begin half-price sushi
this year). We all had our hang-ups, ideosyncracies, and rituals, but instead
of being taboo, we laughed and celebrated them. It was good to &ldquo;belong&rdquo;.
Over time, monday night sushi dates became a thing, runs to Jim&rsquo;s Pizza Box
were common, and we bonded between the hours of work and &ldquo;alone time&rdquo;.</p>

<p>Have you ever seen that YouTube video by that kid Lohanthony where he spins his
leg and calls &ldquo;all basic bitches&rdquo;? You probably have, but if you&rsquo;ve NOT, then
check this out:</p>

<div class="embed-video-container"><iframe src="http://www.youtube.com/embed/dKY0FsUEMyw "></iframe></div>


<p>Somewhere along the line the three of us watched this video and kept referring
back to it (mainly that shit he does with his leg). Eventually, we started
calling each other &ldquo;basic bitches&rdquo;, or just BBs for short (because we ain&rsquo;t
got time for whole words). The name stuck, and Tanny would refer to her BBs
near and far.</p>

<p><strong>Note: I&rsquo;m certainly not stupid enough to presume that A.) we were Tanny&rsquo;s
only friends, B.) we were Tanny&rsquo;s &ldquo;best&rdquo; friends, or C.) I had any monopoly
on insight into Tanny&rsquo;s thoughts &ndash; I&rsquo;m only suggesting that we formed a bond
that we maintained up until this day.</strong></p>

<p>Around 2008, the school district restructured and opened an elementary school
for preschool children through 2nd grade. The district opened a search for an
elementary principal, and it turns out that the best candidate was right under
their noses. Tanny had completed her administrative certificate with other folk
who ended up working in our school district, and so when the position opened up
there was an outpouring of support for her. During that time, I sat on the
&ldquo;Administrative Leadership Team&rdquo; for the school district since the role of
&ldquo;Director of Technology&rdquo; was considered an administrative position (versus a certified or
classified position in the union). Because of this, Tanny asked me questions
how things worked. I won&rsquo;t begin to profess that I was her innermost confidant
(as I said earlier, she had friends who were principals in our school district,
and they were more than capable of answering her questions), but I will say that
we had a super casual and extremely down-to-earth bond that you would have with
someone with whom you&rsquo;d shared all your deepest and darkest secrets (well,
she knew all MY secrets &ndash; to know Tanny is to understand that you never truly
know all there is to know about her). Tanny was a shoe-in for the job, and the
Huron City School district had the smiliest goddamn principal in the tri-state
area :)</p>

<h2>&ldquo;How do YOU get cancer?&rdquo;</h2>

<p>I remember Tanny confessing that she had started feeling strange. She was very
much in-tune with her body, and she had some nagging suspicions about symptoms
that didn&rsquo;t add up. I remember her saying that she pressured her doctor into
doing some additional tests, and, low and behold, they had found some questionable
results.</p>

<p>Now, you&rsquo;ve got to understand, Tanny was the pinnacle of healthy living in my
book. She&rsquo;d been a vegetarian living in the midwest for as long as I knew &ndash;
that is a goddamn feat &ndash; she&rsquo;s been rail-thin forever, and she gave the
appearance of &ldquo;Miss Honey&rdquo; from Matilda to anyone that laid eyes on her. She
lived on positive energy, vegetables, and macaroni and cheese. For all intents
and purposes, she was the poster child for avoiding cancer, but here we were.</p>

<p>Tanny was methodical with everything she&rsquo;d ever done in life. This is the
woman who wouldn&rsquo;t go to Hawaii without a list of things she needed to accomplish,
the woman who would send HERSELF post cards home FROM Hawaii so she could
remember what it felt like being there, and the woman who had binders full
of tabs that separated all her monthly bills. Everything she ever researched
was tabulated and sorted according to whatever index made the most sense, and
she left absolutely no stone unturned; she was simply a voracious consumer of
information (which, frankly, is why I loved her &ndash; being in IT I thrived on
tearing things apart and understanding how the components came together to create a
stronger product). When Tanny was diagnosed with cancer, she knew it was going
to mean big changes for both herself as well as all the employees and students
at the school. She also knew that her privacy was going to be invaded. She
had the desire to tell people personally of her condition, but wanted it to
be a secret so she could control the flow of information. She had it SO planned
out that she made sure to reveal her secret in pairs, so that every person she
told had at least ONE other person they could go to in order to discuss what
they had learned. It was the most methodical goddamn reveal I&rsquo;d seen this side
of Extreme Home Makeover, but it worked.</p>

<p>I remember the day she broke the news to me. I was sitting in her office and
was DETERMINED to keep my shit together. I did not like to show emotion, I
enjoyed playing the jester and keeping things light, and this was entirely too
heavy for my taste.</p>

<p>But I crumbled.</p>

<p>My god did I crumble. I remember tilting my head back and staring up around her
office trying to will the tears back into my eyes. And my breakdown fed her
breakdown, which bothered me even more. Tanny did NOT like to cry in front
of people (though she&rsquo;d tell you that it was perfectly fine to do so). I hold
that same sentiment, so it was one of the hardest things for us to share. To
this day, it&rsquo;s the only time we&rsquo;d seen each other cry, and part of me is happy
that we let each other have the moment. My mind was reeling and all I could
think about was life without Tanny&hellip;and it hurt. It didn&rsquo;t compute. She wasn&rsquo;t
SUPPOSED to have bad things happen to her (except when she tried to fly anywhere
by plane, which was constantly a shitshow) &ndash; she was the pinnacle of optimism
and sunny days keeping the clouds away. I couldn&rsquo;t rationalize it. My entire
life revolves around me rationalizing some behavior I discovered, and this
simply did not add up. You want to piss off a kid who needs to understand their
environment? Give him some of this does-not-compute bullshit and expect them
to live with it. Man did it hurt.</p>

<p>I went home that night and I wrote a letter. I know Tanny still has it
somewhere, and I only VAGUELY remember what I wrote in it, but I poured
every raw emotion I had into it, stuffed it into a card, and left it for her.
It was the only way I could vent the building emotional pressure that I so
didn&rsquo;t want welling up inside me. And that was it. The next day, I was focused
on how she was going to kick ass with treatments and deal with this in true
Tanny-style.</p>

<h2>Cancer treatments are hell, and cisplatin is the devil</h2>

<p>Unfortunately for Tanny, &ldquo;true Tanny-style&rdquo; meant that she was going to go home
and browse the internet looking up the kind of cancer she had. She had told us
all she had cancer, and was somewhat vague about the TYPE of cancer, but that
was it.</p>

<p>As an aside, it&rsquo;s probably pertinent to explain a little something I like to
call &ldquo;The Tanny Truth&rdquo;. Tanny is the most honest individual I have EVER met in
my entire life. Being someone who freaks out MORE when I don&rsquo;t know ANYTHING
about something versus when I know the TRUTH about something (no matter how
terrible or embarrassing it is), I totally appreciated that Tanny would give
me the truth when I asked for it.</p>

<p>But you had to know HOW to ask for it.</p>

<p>You could ask Tanny a question like &ldquo;How is the cancer?&rdquo; (which would be an
incredibly rude question, but let&rsquo;s go with it), and she would respond &ldquo;I feel
great today!&rdquo;. The truth is that she DOES feel great, but she didn&rsquo;t actually
tell you ANYTHING about the cancer. Less experienced individuals would get
swayed by the response and would roll with it &ndash; exactly like she wanted. More
experienced individuals would rephrase the question and get a similar brush
off. After awhile, you recognized when you were getting the Tanny truth and
simply rolled with it because A.) It was Tanny and she was private, or B.)
She was actually trying to protect you. Either way, you&rsquo;re better off just
being deftly redirected like a stream in the woods versus trying to swim
against the current and trying her patience.</p>

<p>So, understanding how The Tanny Truth worked, I didn&rsquo;t pursue the line of
questioning about the type of cancer she had. It wasn&rsquo;t until years (truly,
YEARS) later that I found out exactly what a unique and aggressive form of
cancer she really had. In these immediate days after her diagnosis, she
discovered some truly terrible pictures of the type of cancer she had. I cannot
even begin to fathom what it was like for someone who had lived exceedingly
well and had planned out their life so meticulously to have their mortality
delivered to them courtesy of some scan results. I&rsquo;ll never be able to
understand the power she had to command to fight this MENTAL battle even before
the physical treatments had begun, but, again, this was Tanny and she was a
believer in the power of the self and positive attitudes.</p>

<p>To her credit, had I known the bleak picture the internet painted her at that
time, I would have been even more inconsolable than I had already been that
day in her office. I chatted with a friend recently who went through cancer,
and he summed it up better than I could: &ldquo;When you&rsquo;re in treatment, you&rsquo;re
in survival mode: you&rsquo;re fighting for yourself and focused on getting better.
Cancer is much worse for the people around you than it is for you.&rdquo; Thinking
about this years after the fact, I not only respect her privacy and decisions,
but love her for them.</p>

<p>The first round of treatments Tanny received were hell. The thought process was
that you deliver the harshest treatment immedately following diagnosis when
you&rsquo;re at your healthiest (and with the hopes that you can eradicate the
cancer when it&rsquo;s small). The treatments she received made her lose her hair
and delivered the harshest blow to her body. Here was this woman who couldn&rsquo;t
have weighed more than 110 lbs ANYWAYS, and she was receiving treatment that
made you lose weight and feel weak. Throughout the whole ordeal, she prided
her ability to NOT lose the weight but instead to keep on plan and upbeat.
I don&rsquo;t know how she did it, but she did. I won&rsquo;t dwell on the details, but
she spent the first summer after her first year as principal getting treatments
with the hope that she could return back in the fall. Who DOES that?! Tanny does.</p>

<p>Tanny&rsquo;s hair eventually grew back, she slowly came back to school, and eventually
resumed her role as Principal at Shawnee Elementary. As far as people knew, she
was back to 100% and ready to rock!</p>

<p>The relationship between Tanny and myself became much stronger and closer after
her cancer diagnosis. It was a considerable wake-up call that emphasized how
precious life is. One of Tanny&rsquo;s favorite quotes was:  &ldquo;We&rsquo;re all terminal;
none of us knows our expiration date, so we&rsquo;d better make the most out of every
day that we&rsquo;ve been given.&rdquo; We both knew that, and I think it helped to eliminate
all pretense and allow us to be incredibly candid, open, and honest with each
other in every situation. Also, if you think I&rsquo;m trying to say that &ldquo;I took
care of Tanny,&rdquo; then you&rsquo;re sorely mistaken. That bitch STILL looked out for
me no matter how sick she felt. One of my favorite stories came from a trip to
San Francisco that I had taken in early 2010. I had gone out to San Francisco to
attend a conference, and had managed to leave my FAVORITE HAT at the New Delhi
restaurant on Ellis street. When I arrived back in Huron, I phoned the
restaurant and they had INDEED found my hat &ndash; but now how was I gonna get it?
Sure, they could mail it to me, but serendipity reared its head in the form of
Tanny&hellip;who had plans to visit San Francisco two short weeks after I had
returned. Tanny stopped into the restaurant, picked up my hat, and delivered it
back home to me when she returned. That hat became a staple of my outfit,
and many people I know in technology ONLY know me for that hat. The truth is,
that hat had new meaning to me the instant Tanny delivered it back to me.
Suddenly, it was a simple connection to her and a reminder of how she was
always there for me.</p>

<h2>Schools, fools, and coping tools</h2>

<p>Around the spring to summer of 2010, we were all closing out the school year,
entering into summer, and looking forward to the NICE three months in Ohio! Over
the course of that summer, &ldquo;things&rdquo;, well, how do I put it, &ldquo;HAPPENED&rdquo; between the
Superintendent of our school district and the Principal of another school district.
If you want to read about it, the Sandusky Register website has
<a href="http://www.sanduskyregister.com/fox-fiasco">a whole section dedicated to the situation</a>, and you can even
<a href="http://www.sanduskyregister.com/sites/www.sanduskyregister.com/files/3_Bd_Ex_4.pdf">read all the lovely emails with all the sordid details</a>.</p>

<p>One of my favorite things to do was to visit Tanny in her Principal&rsquo;s office
(which was BRIGHT YELLOW and full of Yodas and smiley faces) and chat about the
events of the day. This was especially fun in the summer because NOBODY was
around and we were able to talk without fear of interruption.</p>

<p>Working at a school district over the summer comes with a certain amount of
&ldquo;flexibility&rdquo; because the students and teachers aren&rsquo;t there, and you&rsquo;re mostly
surrounded by the cleaning/support and front-office staff. Anyone who&rsquo;s a good
administrator will tell you that their slowest AND busiest periods are during
the summer (wrapping up the current year, planning for the next). As the
director of technology, my summers were ABSOLUTELY busy and full &ndash; that&rsquo;s when
I took down systems, experimented with new workflows, and had to get everything
re-imaged and ready by the first day of school. Couple this with cleaning staff
who want to unplug everything to clean, and you have a recipe for some crazy
stuff</p>

<p>Anyways, here we would find ourselves at the end of the day in her office
talking about whom had showed up where, who was getting what work done, and
so on. I&rsquo;m totally fascinated by psychology (but I HATE sociology because
A PERSON is awesome, but PEOPLE, as a group, fucking scare me), and Tanny had
one of the best reads on people and body language that I had ever seen in my
life. She could detect the smallest of tells, and I usually had the background
information.</p>

<p>Previously, I told you that Tanny loved to refer to her &ldquo;elephant brain&rdquo;,
because it was just this steel trap of information. We BOTH grew up in the same
small town in which we were employed, so we BOTH had the same experiences with
the same families. Small town families run deep and you frequently see
generation after generation of family members funneling through the doors of the
schools. We LOVED &ldquo;connecting the dots&rdquo; and making connections about kids, their
parents, and whatnot. This sounds like small-town drama, and, make no mistake
about it &ndash; it totally is, but it&rsquo;s what you do to stay sharp and survive.</p>

<p>At any rate, the nature of my job was to be in every building nearly every day.
Also, I would go take pictures of all the sports and extra-curricular events,
couple that with managing all the user accounts and lots of data entry, as well
as a mind for remembering dumb things, and I basically held lots of names and
connections in my head. I&rsquo;d hear a kid&rsquo;s name, could tell you what grade and
school they were in, what sport they played, and most likely what group of kids
they hung out with. My job was information, and I was good at it because it
FASCINATED me.</p>

<p>Combine my fascination with Tanny&rsquo;s innate abilities and her amazing
&ldquo;elephant brain&rdquo;, and you can probably see where I&rsquo;m going. We would play this
little game of taking things that had happened and trying to correlate them
with other things we knew. For example, maybe someone spraypainted the
building or stole the flag from a flagpole, and all the cameras got were
shadowed glimpses of figures walking, Tanny could CSI the shit out of that
data. We also knew that kids never did ANYTHING alone and never shut up about
the things they did. Put these pieces together (combined with the fact that this
WAS a small town), and there wasn&rsquo;t anything that could get by Tanny. It&rsquo;s no
coincidence that one of Tanny&rsquo;s favorite TV shows was Scandal.</p>

<p>So, come the fall of 2010, and things feel very different in the district. I&rsquo;m
coming up on my 30th birthday, though, and am starting to feel like I&rsquo;ve squeezed
all the life I can out of Huron and my situation. I&rsquo;ve had job offers before,
but this was the first time that I&rsquo;d actually entertained them. In my mind,
Tanny was a big part of why I stayed in Huron. Her friendship meant the world
to me and I&rsquo;ve STILL, to this day, not met anyone else with whom I&rsquo;ve had a deep
connection like I did with Tanny. We could read each other&rsquo;s body language,
facial expressions, and immediately follow the stringy references we would
conjur up to avoid detection. I remember actually having the thoughts that
if Tanny died, I would have to leave Huron and pursue life elsewhere &ndash; I just
wouldn&rsquo;t be able to stay in town and cope without having her there. As it turned
out, this process would happen much sooner than I expected&hellip;</p>

<p>One particularly attractive job offer turned up (the job I currently hold, as
a matter of fact), and I couldn&rsquo;t refuse it. Unfortunately (or fortunately), I
needed to leave Huron and move to Portland, Oregon. Tanny was the first person I told,
and I could tell that even though she was very happy for me, that a part of her
was equally sad that I was jumping ship and leaving town.</p>

<p>Just before this happened, though, another &ldquo;attractive offer&rdquo; had arisen that
caused me to make a difficult decision. I&rsquo;d always known that I was gay, but
never bothered to mention it to anyone else in town. Also, with the
previously-mentioned age gap, there was never anyone around my age that I felt
like I could hang out with. After 30 years, though, I had found someone that
I wanted to spend time with, and I needed to tell someone about it. Tanny was
the first person with whom I ever verbalized my feelings. Shockingly, this was
something she hadn&rsquo;t suspected &ndash; I was actually taken aback that I was able to
put something by the GREAT Tanny Vonthron, because that bitch missed NOTHING!
She was amazingly warm and super supportive, but part of me feels like she
was only this way because she and Brooke always wanted a gay BFF&hellip;</p>

<p><a href="http://garylarizza.com/images/tanny/portland_bin.JPG"><img class="left" src="http://garylarizza.com/images/tanny/portland_bin.JPG" width="400" height="400"></a>
<a href="http://garylarizza.com/images/tanny/portland_bin_sideways.JPG"><img class="left" src="http://garylarizza.com/images/tanny/portland_bin_sideways.JPG" width="400" height="400"></a></p>

<p>As my departure date drew near, Tanny organized a going away party that still
makes me smile :)  The show Portlandia had just become popular, and Portland
was being known as the city where &ldquo;The dream of the 90s was alive&rdquo; &ndash; that was
the theme of my going away party. Tanny had made little Sex in the City name
badges for everyone to wear (whether you were a Carrie or a Samantha, which
were Tanny and Brooke respectively. I can&rsquo;t remember who I was, but at least
I wasn&rsquo;t the whore), and had put together a &ldquo;going away&rdquo; box for me. Picture
a plastic storage bin (or actually LOOK at the picture on the left), but around
the inside wall of the bin she had printed out pictures of 90s CDs, TV shows,
and movies (Friends, Reality Bites, Seinfeld).  Inside I had all the
necessities for moving away &ndash; CDs she had burned with her favorite 90s songs,
books, pictures, and the like. She LOVED making personalized things like this,
and to this day I have that bin exactly the way she left it (mainly because
I&rsquo;m really lazy, but also partly because I&rsquo;m also nostalgic).</p>

<p>Once I had left, our communication was mostly limited to countless group
iMessages, phone calls, and occasional visits to Huron as I was passing through.
Phone conversations were pretty limited because my job mostly consisted of
traveling from one place to another, and I really hate to use the phone. When
we DID get a chance to talk on the phone, though, we tried to talk about everything
BUT Huron and what was happening at home. I think Tanny enjoyed talking to me
because I was outside of Huron and could share stories of things that weren&rsquo;t
going on in her backyard (also &ndash; there&rsquo;s only so much you can take of &ldquo;same shit,
different day.&rdquo;). Even though I was 2000 miles away, modern technology made it
feel like we were still nearby.</p>

<h2>&ldquo;Fox Fiasco&rdquo;</h2>

<p><a href="http://www.sanduskyregister.com/fox-fiasco">Earlier, I mentioned the &lsquo;fox fiasco&rsquo;</a>, which refers back to the
story of the former superintendent of Huron City Schools and his alleged
misdeeds. I have no desire to write anything about the former superintendent,
but I do want to write about how I believe the experience made Tanny feel.
Since there is pending litigation, I&rsquo;ll preface all of this with the statement
that these are my personal beliefs/opinions.</p>

<p>Before that &ndash; some background&hellip;</p>

<p>Tanny Vonthron was certainly the most honest person I&rsquo;ve ever met in life, and
the person whose moral compass consistently pointed due north for as long as
I&rsquo;d known her. Tanny&rsquo;s opinion of people aligned with how honest, moral, and
caring they were (among other things). She valued these three characteristics
above all others (in my opinion), and tended to surround herself with people
who demonstrated these three core values. Before she was a principal, Tanny
was an intervention specialist and she dealt with children who had any number
of issues that required special attention. Her favorite students were those who
landed on the &ldquo;I&rsquo;m incredibly honest, literal, and have problem reading and
expressing emotion&rdquo; level of the autism spectrum. She genuinely loved these
people because you know at any point in time where you stood with them (and,
if you didn&rsquo;t know, just ask them and they&rsquo;d tell you without hesitation).
Tanny was the personification of &ldquo;zero judgement&rdquo;, and loved these students
for every reason why others felt they were &ldquo;disabled&rdquo;. Tanny had this amazing
ability to reach them on their level, and I believe they genuinely felt she
was interested and willing to do anything to help them out (undoubtedly because
she actually WAS GENUINELY INTERESTED AND WILLING TO DO ANYTHING TO HELP THEM
OUT!). In the time I shared with her at school, I learned an absolute ton
about both the students and the autism spectrum on which they landed. Tanny
was super careful not to break confidentiality or share sensitive information,
but would talk in generalities and show me the &ldquo;human&rdquo; side of the human
condition. Looking back, I identified VERY STRONGLY with her students because
I actually empathized with them on many levels, and I feel that Tanny not only
loved my idiosyncrasies but tried to show me how ALL of us have our issues and
we&rsquo;re ALL a bit &lsquo;special.&rsquo;</p>

<p><strong>Anyways, we&rsquo;ve established that Tanny valued honesty and morality, which you
would THINK would be the characteristics LEAST exhibited by someone who
allegedly had an affair during school hours &ndash; right?</strong></p>

<p>I feel very strongly that Tanny felt betrayed by her former boss (&lsquo;betrayed&rsquo;
isn&rsquo;t nearly a strong enough word, in my opinion), especially considering these
events occurred while she was in treatment for cancer. I think she was even
more embarrassed when rumors started flying about her possible involvement with
her former boss. The rumors were categorically false (like I said, Tanny had
a moral compass that was cemented &lsquo;due north&rsquo;), but the accusations were enough
to cut her deeply (and, again, even though she felt upset, she never let it
show).</p>

<p>I&rsquo;d love to say more, but I&rsquo;d also not like to feed into the ego of a few, so
we&rsquo;ll just leave it like that.</p>

<h2>The past year</h2>

<p>Tanny was diagnosed in April, 2009, and so in April, 2013, she celebrated her
4 year &ldquo;cancerversary&rdquo;.  She celebrated at Nagoya with some sushi (as one
does), but it also felt (to ME, at least) like an odd omen. Short of losing her
hair, you would never know that Tanny had any health issues. All of the sudden,
though, now she&rsquo;s hitting these &lsquo;anniversaries&rsquo; date and (thus far) beating the odds.
That wasn&rsquo;t how Tanny wanted to see it, though.</p>

<p>To know Tanny, and to share time with Tanny, was to concede to a couple of
&lsquo;conditions&rsquo; to her condition:</p>

<ul>
<li>Be positive</li>
<li>Be present</li>
<li>No goodbyes</li>
</ul>


<p>The first rule was non-negotiable. If you were being negative, she was removing
herself from the situation (see the quote at the top of the page).</p>

<p>The second is, as <em>I</em> see it, a more recent addition. See, Tanny was also the
plan-iest person I&rsquo;ve ever met. She had plans for the present, plans for the future,
plans for dinner, and plans to update her plans. She had binders, with tabs and
highlighted passages, for damn near everything. But, as the saying goes about
best laid plans, that was somewhat relaxed after Tanny was diagnosed. Sure, she
still had her plans, but she also did as MUCH as she could to absorb every
effervescent element around her as often as she could. Imagine her hugging a
card to her chest and closing her eyes for about 5 seconds &ndash; she would literally
try to pull the emotions and the moment into her to feel them race up her spine
and tingle those little neckhairs (See &ndash; that&rsquo;s my attempt writing to elicit
emotion; Mary Alice Harpster would be SO proud&hellip;). Tanny would clip all of
these little articles about teaching yourself to &lsquo;be more present&rsquo;, which,
at the time, I thought was kinda weird&hellip;but, then again, nobody was holding
a clock above MY head. As someone who is impulsive and wanders around life jumping
from one adventure to the next, it was kinda awesome to see her join me in what
SHE called &lsquo;being present&rsquo; but what I called &lsquo;another day&rsquo; :)</p>

<p>Finally, Tanny did NOT want to go through the &lsquo;goodbye&rsquo; phase. From the start,
she did as much as she could to not be PERCEIVED as &lsquo;sick&rsquo;. She hated losing
her hair, still kept up her immaculate appearance (and appearances), and fought
as hard as she could to remain &lsquo;just Tanny&rsquo;. She even had rules against &lsquo;sad
eyes&rsquo; (You know how when someone tells you something, like &lsquo;my dog just died&rsquo;,
and you immediately give them the &lsquo;sad eyes&rsquo;? You know what I&rsquo;m talking about &ndash;
you cock your head just slightly, raise your eyebrows, and affect that &lsquo;oh
my goodness&rsquo; tone. Yeah, Tanny would have none of that shit). So even if Tanny
wasn&rsquo;t doing well, you weren&rsquo;t allowed to gush about the perceived enormity of
it all &ndash; that would be conceding that she <em>WASN&rsquo;T</em> fighting and, thus, was
conceding to this invisible biological threat. No, she wanted absolutely none
of that.</p>

<p>I&rsquo;m not say that we were all walking on eggshells for the past year, I&rsquo;m just
saying that for the first time since she was initially diagnosed it started to
feel&hellip;I dunno&hellip;more &lsquo;real&rsquo;? Because Tanny was who she was (because &lsquo;a fighter&rsquo;
is the clichéd emobodiment of &lsquo;an understatement&rsquo;), she demanded normalcy. And
normalcy is exactly what I (we) gave her. I kept flying around the damn world
preaching the good word of Puppet Labs, and she kept opening the school in
the morning, giving out smiley face awards, and seeing the kids off at the end
of the day (so to speak). I had something of a spoken &lsquo;unspoken&rsquo; rule with a
couple of close friends that they were to let me know as SOON as things were
&lsquo;not going well&rsquo; so that I could fly home to be there (knowing full-well that
Tanny would do no such thing). I never really got that &lsquo;call&rsquo;, but I did make
the call to come home between March 7th and 11th, 2014 based on a couple of
conditions.</p>

<p><a href="http://garylarizza.com/images/tanny/last_text_to_me.PNG"><img class="left" src="http://garylarizza.com/images/tanny/last_text_to_me.PNG" width="400" height="400"></a>
<a href="http://garylarizza.com/images/tanny/last_call.PNG"><img class="left" src="http://garylarizza.com/images/tanny/last_call.PNG" width="400" height="400"></a>
The last week of February I was working in Texas and was texting back and
forth with Tanny. We didn&rsquo;t really text TOO often, but the texts were coming
with a bit more frequency. On that thursday, the 27th, Tanny sent me a text
telling me to &lsquo;Call me when you can&rsquo;, which was really rare for Tanny (we
talked, but usually on weekends when she had downtime, and usually only to
catch up. This was like 3pm on a thursday, though, and so was a bit out of
character. When I called her, we talked about a bunch of random things
(ranging from happenings in Huron, where I was traveling, and the like), but
it was really laid-back and&hellip;I dunno&hellip;free of distraction? Tanny constantly
had texts, calls, people stopping by her office, and whatnot &ndash; this conversation
had NONE of that, and we just talked about whatever came up for about 40 minutes.
The conversation came to a close, and it REALLY started to feel like Tanny was
injecting a subtext that felt really&hellip;quotable (You know, things like &lsquo;well,
you know I want you to do whatever makes you happiest, and&hellip;&rsquo; that had an
air of finality). She ended the conversation with &ldquo;I love you&rdquo; (which was
something we started doing within the past year), and I felt really good that
I had been able to talk to her (since we hadn&rsquo;t really spoken since Christmas &ndash;
it was all texts).</p>

<p>After that call on Thursday, I would never received another call or text from Tanny
again.</p>

<p>I definitely started to get worried the following week when I didn&rsquo;t hear from
Tanny (but knew she liked her privacy, so tried to give her some space). After
hearing that another mutual friend had felt the same way and had booked a flight
home, I decided to do the same for the upcoming weekend. It was convenient that
my mother&rsquo;s birthday was the following week on March 12th, so that made for a
reasonable excuse, but, frankly, most people had started wondering about Tanny
over recent weeks after she&rsquo;d been progressively more absent from work. If you
were to take a drive around Huron, you would have seen yellow smiley faces
everywhere (I&rsquo;m talking flags, wood signs, stickers &ndash; you name it and people
were displaying it to support Tanny). It&rsquo;s definitely NOT hyperbole to say
that the community loved Tanny, and everyone wanted to show their support in
some way. Some suggested ideas were definitely crazier than others, but everyone&rsquo;s
heart was in the right place.</p>

<p>It would be dramatic to say that I flew home, sat beside her bed, held her
hand, and confessed every way in which she changed my life, but the truth is
honestly much more boring than that &ndash; remember, to know Tanny is to give up
your ability to deliver the &lsquo;goodbye speech&rsquo; to her. And don&rsquo;t think she wasn&rsquo;t
above sending people who started down that road RIGHT out of her room!
I definitely heard stories of her shoo-ing people out who began
very-clearly-rehearsed &lsquo;final words&rsquo; to her (I think THAT, most of ALL, makes
me laugh the loudest) :)</p>

<p>I would fly out of Cleveland bound for a work gig in Denver on March 11th, thus
missing her death by one day (yep, if you&rsquo;re keeping score, she died on my
mother&rsquo;s birthday).  There are very few things about me that I&rsquo;m guarded
about &ndash; most of everything about my life is sewn on my sleeve in very colorful
thread &ndash; but my feelings for and about Tanny are the some of the most precious
things I carry (ironic, considering this post).</p>

<h2>Immediately following</h2>

<p>Life was kind of a blur after Tanny died.  I don&rsquo;t know where to begin in
trying to describe how I felt, but I will share ONE story from that time
period:  One of the things I&rsquo;m very honored to have been able to do was
<a href="http://www.sanduskyregister.com/obituary/5434676">Tanny&rsquo;s Obituary</a>.  Tanny&rsquo;s
family had asked me to help out with it, and it was fun to open a Google doc,
start writing, and watch the rest of her family write comments and jokes while
I was giving birth to the thing. To say it was rough to write was an
understatement.  Tanny liked things to be&hellip;well&hellip;perfect. When she composed
a document, every word was chosen for a purpose, every sentence tied the
previous to the next, and the entire body of work was meant to convey a single
message. I could hear Tanny laughing at me as I spent tens of minutes fighting
over a single word. I could LITERALLY hear her family laughing at me as
I re-wrote entire sentences because they felt wrong.  Thinking I had a final
document, I called Brooke and asked her to read it with me.  I&rsquo;m REALLY glad
I did that because Brooke was able to give me some pointers on things I hadn&rsquo;t
even considered.  In the end, several people had a hand in putting that
document together, and I feel, like Tanny&rsquo;s life, it was made better by all the
people that were close with her.</p>

<h2>Life without Tanny</h2>

<p>It has taken me over two months to put this post together simply because
it&rsquo;s a painful thing to keep revisiting. I&rsquo;ve honestly written this post
on something like 20+ different flights, in countless states and countries,
and, always with the goal of &lsquo;finishing today&rsquo;. Part of me believes that I
can&rsquo;t finish it because I don&rsquo;t have Tanny around to help me work out my
thoughts :)</p>

<p>What I&rsquo;ve come to understand, though, is that the best parts of myself came
directly from Tanny. I made a conscious decision to not lie after watching her
model this behavior for 39 years (and, also, because I can&rsquo;t remember where
I put my KEYS let alone what I told someone, somewhere). Watching Tanny deal
with difficult situations and model behavior for her students taught me how to
be able to respectfully diffuse a situation. I could go on forever, but the
core idea was that she was THE ONE person that I had in my life that I KNEW
DEFINITIVELY would only do something with good intentions. Anytime Tanny said
or did something that I reacted even slightly negatively to, it helped me
to discover the flawed thinking in myself (the idea being that if I was upset
over something she said/did, but that I KNEW that Tanny didn&rsquo;t intend it to
be upsetting, what part of me was misinterpreting things?). I had absolutely
no idea how formative this was&hellip;until it wasn&rsquo;t around anymore.</p>

<p>I&rsquo;ve also reached for my phone way too many times these past months &ndash; I will
see something, open up a text, and start writing a snarky little note&hellip;until
I remember. Sundays are particularly hard because that&rsquo;s when we usually talked.
Every time I meet a new person, or do something worth a story (which, frankly,
if you KNOW me happens almost hourly), I&rsquo;m reaching for my phone to tell someone
who&rsquo;s not going to write back again. It hurts.</p>

<p>Finally, I tend to process things a bit more slowly than normal people. When
Tanny died, I did everything I could to keep my shit together and &lsquo;stay strong&rsquo;
for everyone else.  I remember TRYING to have a breakdown and failing kinda
miserably at it.  It wasn&rsquo;t until very recently where I was talking somewhat
casually about Tanny that I just got hit with a wave of emotion. It hurt, but
it felt&hellip;welcoming. Coping is good (and, as The Shins say, caring is creepy).</p>

<h2>How do I even end this?</h2>

<p>Honestly, I have no idea what this post was about other than something of a
love letter for a friend. It&rsquo;s also helped me work out the MANY ways Tanny
helped me that I hadn&rsquo;t even remotely considered. It&rsquo;s also helpful in
answering the question &ldquo;Who was &lsquo;Tanny&rsquo; on your tattoo?&rdquo;</p>

<p><a href="http://garylarizza.com/images/tanny/tattoo.png"><img class="left" src="http://garylarizza.com/images/tanny/tattoo.png" width="400" height="400"></a>
Yep, after Tanny died, a couple of us got tattoos.  The funny thing is that
I often talked with Tanny about wanting a tattoo on my wrists.  I even went
so far as to draw/paint them there a couple of times. My wrists are my &lsquo;prized
location&rsquo; because they&rsquo;re right out there (and I talk with my hands&hellip;I&rsquo;m Italian;
what do you want from me?). Ultimately I took that from a letter Tanny sent &ndash; it&rsquo;s
her handwriting and all &ndash; and it&rsquo;s really poignant because, as I mentioned
before, we didn&rsquo;t start saying &ldquo;I love you&rdquo; until recently, and it&rsquo;s something
I have hangups saying.  With Tanny, it was meaningful.  Every laptop I&rsquo;ve had
since Tanny was diagnosed has had a yellow smiley face on the right wrist rest,
and so I thought it was appropriate to put her writing there too.  I love that
I can see it everyday, and it&rsquo;s been really awesome to get daily reminders.</p>

<p>In the &lsquo;end&rsquo; (of this post, at least), I have to say that one of the biggest
things I pieced together with Tanny was the concept of &lsquo;regret&rsquo; being arguably
the worst word in the English language. It&rsquo;s something that basically means you
wish a period of time didn&rsquo;t exist.  Something SO TERRIBLE happened that you
not only want to have NEVER experienced it, but you feel like the lessons you
learned that time would be better spent by doing the situation all over again.</p>

<p>I truthfully have no regret in my life for any decision I&rsquo;ve ever made.</p>

<p>Tanny once said that &ldquo;I&rsquo;m thankful for every good thing, every bad thing, and
every wise, stupid, rash, calculated, and wonderful decision you&rsquo;ve ever made
in your life because they&rsquo;ve made you the person you are, and they&rsquo;ve brought
you to this place, at this very moment, in life.&rdquo; I don&rsquo;t think I could say it
better &ndash; my life is at a wonderful place right now, and most of that is due
to the best person I&rsquo;ll ever know:  Tanny.</p>

<h2>-UPDATE-</h2>

<p>As is usually the case, I remember things (or learned things) after initially
making this post.  I had WARNED you that I didn&rsquo;t feel like this post was
finished, and so here we go&hellip;</p>

<h3>Portland Care Package</h3>

<p>I mentioned above that Tanny made me this lovely box of 90s nostalgia after
she found out that I was going to be moving to Portland (where the dream of
the 90s is STILL alive).  So, it turns out that&rsquo;s not entirely true&hellip;and
by that I mean that Shannon Smith came up with the idea :) I got a funny
Facebook message from Shannon filling me in on the deets and laughing at how
much I attributed to Tanny.  Personally, it makes for a better story, so Shannon&rsquo;s
just gonna have to cope and deal :)</p>

<h3>The numbers</h3>

<p>One of the things Tanny and I shared was this fascination with numbers. I have a
huge obsession with reducing everything to a single number and deciding if it&rsquo;s a
&lsquo;good&rsquo; number. The numbers 3, 9, and 11 were, for whatever reason, good numbers
for me (3 being a good number, 3 threes is 9, so THAT&rsquo;s a great number, and 11
is not only the date in which I was born, but is one of those &lsquo;special numbers&rsquo;).
I remember getting somewhat into numerology in my 20s (didn&rsquo;t we all&hellip;sigh), but,
honestly, the thing that REALLY gave me fucked-up superstitions was playing Craps
up at Casino Windsor with my dad and his friend Scott. Imagine a game FULL OF
numbers, and certain numbers that can cause you to lose all your money if they
come up (incidentally, if you know nothing about Craps you probably think that 7
is a REALLY good number&hellip;it&rsquo;s actually the opposite. The number 7 is SO BAD that
you can&rsquo;t even say it around a table or you&rsquo;ll get filthy looks. But I digress&hellip;).</p>

<p>Anyways, I had a habit of taking certain numbers and breaking them down to a
single number.  My old address was 517, and 5 + 1 + 7 is 13. Taking 13 and
breaking it down gets you 1 + 3 which equals 4, so my house &lsquo;was a 4&rsquo;. Weird?
Sure. But it&rsquo;s something I did in silent forever.  Even now when I stay in hotels
(which is weekly), the first thing I do is break the room number down into a single
number&hellip;and sometimes that&rsquo;s the ONLY way I remember my room number (&ldquo;Let&rsquo;s see,
it was an 8, I&rsquo;m on the fifth floor, and it&rsquo;s down this hall&hellip;ah yeah, it was room 503&rdquo;).</p>

<p>I remember one day Tanny and I went out to eat (at Marconi&rsquo;s, incidentally) where
we shared our love of numbers.  Tanny&rsquo;s special number was 33.  Why?  I honestly
don&rsquo;t remember exactly, but I believe it was because 33 was one of the master
numbers (like 11 is&hellip;which is my number).  Also the number 33 always came up
for Tanny, and she used it like a waypoint on the road of life.  We&rsquo;d always
get a random text from Tanny anytime she ran into a 33 SOMEWHERE, and she had
a metal number 33 in her car that had fallen off of one of the old lockers
(and was given to her I THINK by Mr. T &ndash; John Turinsky &ndash; but I could be
wrong).  Sometimes we would get a text at 3:33 just to say hi.  I really
miss those texts&hellip;</p>

<p>Tanny really didn&rsquo;t make TOO much of a secret about this, but it was definitely
&lsquo;her number&rsquo;.  Since then, I see 33s all over the place, and it&rsquo;s a friendly
reminder of her.  If you want to be let in on another secret, check out the
Sandusky Register website and look at the timestamps on any article that was
posted about her&hellip;</p>

<h3>The threesome and Tanny Time, the redux</h3>

<p>I alluded above that at times it felt like Brooke, Tanny, and I were connected
at the hip (and, like I said, that&rsquo;s not to say that Tanny wasn&rsquo;t connected with
ALL her other friends&hellip;it just felt like that to me).  We were SO connected that
John Ruf, when he was principal at HHS, would call us &ldquo;The Threesome&rdquo;.  Whatever
that meant.  A gay man and his two besties did not an awesome threesome make, but
whatever.  ANYWAYS, when I accepted the postion in Portland it felt like I was
breaking up the band (though it didn&rsquo;t happen like that, thanks to the marvels of
modern technology and Facetime).  I remember even getting a text that Tony Munafo,
who was vice principal at the high school at the time, was &ldquo;applying to be a new
member of The Threesome&rdquo;.  Let&rsquo;s just say that didn&rsquo;t end very well (#seatstaken).</p>

<p>Before I left, our threesome was at its peak.  We all worked pretty late (because
we liked the quiet and got work done when people weren&rsquo;t bugging us), so it was
common to get a call at my extension from Tanny and have it turn out to be a
three-way bullshit session.  Tanny would eventually tell us to meet her in her
office at like 5:30 to go get Jim&rsquo;s, Marconi&rsquo;s, or whatever local dish we were
craving, but we ALL knew that if Tanny said 5:30 it usually meant around 6:45.
Tanny operated on Tanny Time (I mentioned before that &ldquo;Tanny Time&rdquo; was the time</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Functional Puppet Workflow Part 3b: More R10k Madness]]></title>
    <link href="http://garylarizza.com/blog/2014/03/07/puppet-workflow-part-3b/"/>
    <updated>2014-03-07T10:00:00-06:00</updated>
    <id>http://garylarizza.com/blog/2014/03/07/puppet-workflow-part-3b</id>
    <content type="html"><![CDATA[<p><a href="http://garylarizza.com/blog/2014/02/18/puppet-workflow-part-3/">In the last workflows post,</a> I talked about dynamic Puppet
environments and introduced R10k, which is an awesome tool for mapping modules
to their environments which are dynamically generated by git branches. I didn&rsquo;t
get out everything I wanted to say because:</p>

<ul>
<li>I was tired of that post sitting stale in a Google Doc</li>
<li>It was already goddamn long</li>
</ul>


<p>So because of that, consider this a continuation of that previous monstrosity
that talks about additional uses of R10k beyond the ordinary</p>

<h2>Let&rsquo;s talk Hiera</h2>

<p>But seriously, let&rsquo;s not actually talk about what Hiera does since
<a href="http://docs.puppetlabs.com/hiera/1/complete_example.html">there are better docs</a> out there for that. I&rsquo;m
also not going to talk about WHEN to use Hiera because
<a href="http://garylarizza.com/blog/2013/12/08/when-to-hiera/">I&rsquo;ve already done that before.</a> Instead, let&rsquo;s talk about a workflow
for submitting changes to Hiera data and testing it out before it enters into
production.</p>

<p>Most people store their Hiera data (if they&rsquo;re using a backend that reads Hiera
data from disk anyways) in separate repos as their Puppet repo. Some DO tie the
Hiera datadir folder to something like the main Puppet repo that houses their
<code>Puppetfie</code> (if they&rsquo;re using R10k), but for the most part it&rsquo;s a separate
repo because you may want separate permissions for accessing that data.
For the purposes of this post, I&rsquo;m going to refer to
<a href="https://github.com/glarizza/hiera_environment">a repository I use for storing Hiera data</a> that&rsquo;s out on Github.</p>

<p>The next logical step would be to integrate that Hiera repo into R10k so R10k can
track and create paths for Hiera data just like it did for Puppet.</p>

<p><strong>NOTE: Fundamentally, all that R10k does is checkout modules to a specific
path whose folder name comes from a git branch. PUPPET ties its environment
to this folder name with some <code>puppet.conf</code> trickery. So, to say that R10k
&ldquo;creates dynamic environments&rdquo; is the end-result, but not the actual job
of the tool.</strong></p>

<p>We COULD add Hiera&rsquo;s repository to the <code>/etc/r10k.yaml</code> file to track and
create folders for us, and if we did it EXACTLY like we did for Puppet we
would most definitely run into <a href="https://github.com/adrienthebo/r10k/issues/48">this R10k bug</a> (AND,
<a href="https://github.com/adrienthebo/r10k/issues/90">it comes up again in this bug</a>).</p>

<p><strong>UPDATE: So, I originally wrote this post BEFORE R10k version 1.1.4 was
released. Finch released version 1.1.4 which FIXES THESE BUGS&hellip;so the workflow
I&rsquo;m going to describe (i.e. using prefixing to solve the problem of using
multiple repos in <code>/etc/r10k.yaml</code> that could possibly share branch names)
TECHNICALLY does NOT need to be followed &lsquo;to the T&rsquo;, as it were. You can
disable prefixing when it comes to that step, and modify
<code>/etc/puppetlabs/puppet/hiera.yaml</code> so you don&rsquo;t prepend &lsquo;hiera_&rsquo; to the
path of each environment&rsquo;s folder, and you should be totally fine&hellip;you know,
as long as you use version 1.1.4 or greater of R10k.  So, be forewarned</strong></p>

<p>The issue is those bugs is that R10k collects the names of ALL the environments
from ALL the sources at once, so if you have multiple source repositories and
they share branch names, then you have clashes (since it only stores ONE branch
name internally). The solution that Finch came up with was prefixing (or,
prefixing the name of the branch with the name of the source). When you prefix,
however, it creates a folder on-disk that matches the prefixed name (e.g.
NameOfTheSource_NameOfTheBranch ). This is actually fine since we&rsquo;ll catch it
and deal with it, but you should be aware of it. Future versions of R10k may
most likely deal with this in a different manner, so make sure to check out the
R10k docs before blindly copying my code, okay? (Update: See the previous, bolded
paragraph where I describe how Finch DID JUST THAT).</p>

<p><a href="http://garylarizza.com/blog/2014/02/18/puppet-workflow-part-3/">In the previous post</a> I setup a file called <code>r10k_installation.pp</code>
to setup R10k. Let&rsquo;s revisit that manifest it and modify it for
<a href="https://github.com/glarizza/hiera_environment">my Hiera repo:</a></p>

<figure class='code'><figcaption><span>/var/tmp/r10k_installation.pp </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='puppet'><span class='line'><span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;r10k&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">version</span>           <span class="p">=&gt;</span> <span class="s1">&#39;1.1.4&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">sources</span>           <span class="p">=&gt;</span> <span class="p">{</span>
</span><span class='line'>    <span class="s1">&#39;puppet&#39;</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span><span class='line'>      <span class="s1">&#39;remote&#39;</span>  <span class="p">=&gt;</span> <span class="s1">&#39;https://github.com/glarizza/puppet_repository.git&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;basedir&#39;</span> <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/environments&quot;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;prefix&#39;</span>  <span class="p">=&gt;</span> <span class="ss">false</span><span class="p">,</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="s1">&#39;hiera&#39;</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span><span class='line'>      <span class="s1">&#39;remote&#39;</span>  <span class="p">=&gt;</span> <span class="s1">&#39;https://github.com/glarizza/hiera_environment.git&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;basedir&#39;</span> <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/hiera&quot;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;prefix&#39;</span>  <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="nt">purgedirs</span>         <span class="p">=&gt;</span> <span class="p">[</span><span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/environments&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="nt">manage_modulepath</span> <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">modulepath</span>        <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/environments/</span><span class="se">\$</span><span class="s2">environment/modules:/opt/puppet/share/puppet/modules&quot;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>NOTE: For the duration of this post, I&rsquo;ll be referring to Puppet Enterprise
specific paths (like <code>/etc/puppetlabs/puppet</code> for $confdir). Please do the
translation for open source Puppet, as R10k will work just fine with either
the open source edition or the Enterprise edition of Puppet</strong></p>

<p>You&rsquo;ll note that I added a source called &lsquo;hiera&rsquo; that tracks my Hiera
repository, creates sub-folders in <code>/etc/puppetlabs/puppet/hiera</code>, and enables
prefixing to deal with the bug I mentioned in the previous paragraph. Now,
let&rsquo;s run Puppet and do an R10k synchronization:</p>

<figure class='code'><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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# puppet apply /var/tmp/r10k_installation.pp
</span><span class='line'>Notice: Compiled catalog for master1 in environment production in 1.78 seconds
</span><span class='line'>Notice: /Stage[main]/R10k::Config/File[r10k.yaml]/content: content changed '{md5}c686917fcb572861429c83f1b67cfee5' to '{md5}69d38a14b5de0d9869ebd37922e7dec4'
</span><span class='line'>Notice: Finished catalog run in 1.24 seconds
</span><span class='line'>
</span><span class='line'>[root@master1 puppet]# r10k deploy environment -pv
</span><span class='line'>[R10K::Task::Deployment::DeployEnvironments - INFO] Loading environments from all sources
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment hiera_testing
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment hiera_production
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment hiera_master
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment production
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying make into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying concat into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ruby into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying make into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying concat into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ruby into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment master
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment garysawesomeenvironment
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying make into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying concat into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ruby into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment development
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Deployment::PurgeEnvironments - INFO] Purging stale environments from /etc/puppetlabs/puppet/environments
</span><span class='line'>[R10K::Task::Deployment::PurgeEnvironments - INFO] Purging stale environments from /etc/puppetlabs/puppet/hiera
</span><span class='line'>
</span><span class='line'>[root@master1 puppet]# ls /etc/puppetlabs/puppet/hiera
</span><span class='line'>hiera_master  hiera_production  hiera_testing
</span><span class='line'>
</span><span class='line'>[root@master1 puppet]# ls /etc/puppetlabs/puppet/environments/
</span><span class='line'>development  garysawesomeenvironment  master  production</span></code></pre></td></tr></table></div></figure>


<p>Great, so it configured R10k to clone the Hiera repository to
<code>/etc/puppetlabs/puppet/hiera</code> like we wanted it to, and you can see that with
prefixing enabled we have folders named &ldquo;hiera_${branchname}&rdquo;.</p>

<p>In Puppet, the magical connection that maps these subfolders to Puppet
environments is in <code>puppet.conf</code>, but for Hiera that&rsquo;s the <code>hiera.yaml</code> file.
I&rsquo;ve included that file in my <a href="https://github.com/glarizza/hiera_environment">Hiera repo</a>, so let&rsquo;s look at the
copy at <code>/etc/puppetlabs/puppet/hiera/hiera_production/hiera.yaml</code>:</p>

<figure class='code'><figcaption><span>/etc/puppetlabs/puppet/hiera/hiera_production/hiera.yaml </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='yaml'><span class='line'><span class="nn">---</span>
</span><span class='line'><span class="l-Scalar-Plain">:backends</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">yaml</span>
</span><span class='line'><span class="l-Scalar-Plain">:hierarchy</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{clientcert}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{environment}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">global</span>
</span><span class='line'>
</span><span class='line'><span class="l-Scalar-Plain">:yaml</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="l-Scalar-Plain">:datadir</span><span class="p-Indicator">:</span> <span class="s">&#39;/etc/puppetlabs/puppet/hiera/hiera_%{environment}/hieradata&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>The magical line is in the <code>:datadir:</code> setting of the <code>:yaml:</code> section; it
uses <code>%{environment}</code> to evaluate the environment variable set by Puppet and
set the path accordingly.</p>

<p>As of right now R10k is configured to clone Hiera data from a known repository
to <code>/etc/puppetlabs/puppet/hiera</code>, to create sub-folders based on branches to
that repository, and to tie data provided to each Puppet environment to the
respective subfolder of <code>/etc/puppetlabs/puppet/hiera</code> that matches the pattern
of <strong>&ldquo;hiera_(environment_name)&rdquo;</strong>.</p>

<h3>The problem with <code>hiera.yaml</code></h3>

<p>You&rsquo;ll notice that each subfolder to <code>/etc/puppetlabs/puppet/hiera</code> contains
its own copy of <code>hiera.yaml</code>.  You&rsquo;re probably drawing the conclusion that
each Puppet environment can read from its own <code>hiera.yaml</code> for Hiera configuration.</p>

<p>And you would be wrong.</p>

<p><a href="http://projects.puppetlabs.com/issues/11784">For information on this bug, check out this link.</a> You&rsquo;ll see
that we provide a &lsquo;hiera_config&rsquo; configuration option in Puppet that allows
you to specify the path to <code>hiera.yaml</code>, but Puppet loads that config as
singleton, which means that it&rsquo;s read initially when the Puppet master process
starts up and it&rsquo;s NOT environment-aware. The workaround is to use one
<code>hiera.yaml</code> for all environments on a Puppet master but to dynamically change
the <code>:datadir:</code> path according to the current environment (in the same way that
dynamic Puppet environments abuse &lsquo;$environment&rsquo; in <code>puppet.conf</code>). You gain
the ability to have per-environment changes to Hiera data but lose the ability
to do things like using different hierarchies for different environments. As
of right now, if you want a different hierarchy then you&rsquo;re going to need to
use a different master (or do some hacky things that I don&rsquo;t even want to
BEGIN to approach in this article).</p>

<p>In summary &ndash; there will be a hiera.yaml per environment, but they will not
be consulted on a per-environment basis.</p>

<h3>Workflow for per-environment Hiera data</h3>

<p><a href="http://garylarizza.com/blog/2014/02/18/puppet-workflow-part-3/">Looking back on the previous post,</a> you&rsquo;ll see that the workflow
for updating Hiera data is identical to the workflow for updating code to your
Puppet environments.  Namely, to create a new environment for testing Hiera
data, you will:</p>

<ul>
<li>Push a branch to the Hiera repository and name it accordingly (remembering
that the name you choose will be a new environment).</li>
<li>Run R10k to synchronize the data down to the Puppet master</li>
<li>Add your node to that environment and test out the changes</li>
</ul>


<p>For existing environments, simply push changes to that environment&rsquo;s branch
and repeat the last two steps.</p>

<p><strong>NOTE: Puppet environments and Hiera environments are linked &ndash; both tools use
the same &lsquo;environment&rsquo; concept and so environment names MUST match for the data
to be shared (i.e. if you create an environment in Puppet called &lsquo;yellow&rsquo;, you
will need a Hiera environment called &lsquo;yellow&rsquo; for that data).</strong></p>

<p>This tight-coupling can cause issues, and will ultimately mean that certain
branches are longer-lived than others. It&rsquo;s also the reason why I don&rsquo;t use
defaults in my <code>hiera()</code> lookups inside Puppet manifests &ndash; I WANT the early
failure of a compilation error to alert me of something that needs fixed.</p>

<p>You will need to determine whether this tight-coupling is worth it for your
organization to tie your Hiera repository directly into R10k or to handle it
out-of-band.</p>

<h2>R10k and monolithic module repositories</h2>

<p>One of the first requirements you encounter when working with R10k is that your
component modules need to be stored in their own repositories.  That convention
is still relatively new &ndash; it wasn&rsquo;t so long ago that we were recommending that
modules be locked away in a giant repo. Why?</p>

<ul>
<li>It&rsquo;s easier to clone</li>
<li>The state of module reusability was poor</li>
</ul>


<p>The main reason was that it was easier to put everything in one repo and clone
it out on all your Puppet master servers. This becomes insidious as your module
count rises and people start doing lovely things like committing large binaries
into modules, pulling in old versions of modules they find out on the web, and
the like. It also becomes an issue when you start needing to lock committers
out of specific directories due to sensitive data, and blah blah blah blah&hellip;</p>

<p>There are better posts out there justifying/villafying the choice of one or
multiple repositories, this section&rsquo;s meant only to show you how to incorporate
a single repository containing multiple modules into your R10k workflow.</p>

<p><a href="http://garylarizza.com/blog/2014/02/18/puppet-workflow-part-3/">From the last post</a> you&rsquo;ll remember that the <code>Puppetfile</code> allows
you to tie a repository, and some version reference, to a directory using
R10k. Incorporating a monolithic repository starts with an entry in the
<code>Puppetfile</code> like so:</p>

<figure class='code'><figcaption><span>Puppetfile </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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;my_big_module_repo&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s2">&quot;git://github.com/glarizza/my_big_module_repo.git&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;1.0.0&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>NOTE: That git repository doesn&rsquo;t exist. I don&rsquo;t HAVE a monolithic repo to
demonstrate, so I&rsquo;ve chosen an arbitrary URI. Also note that you can use ANY
name you like after the <code>mod</code> syntax to name the resultant folder &ndash; it doesn&rsquo;t
HAVE to mirror the URI of the repository.</strong></p>

<p>Adding this entry to the <code>Puppetfile</code> would checkout that repository to
wherever all the other modules are checked out with a folder name of
&lsquo;my_big_module_repo&rsquo;. Within that folder would most-likely (again, depending
on how you&rsquo;ve laid out your repository) contain subfolders containing Puppet
modules. This entry gets the modules onto your Puppet master, but it doesn&rsquo;t
make Puppet aware of their location. For that, we&rsquo;re going to need to add an
entry to the &lsquo;modulepath&rsquo; configuration item in <code>puppet.conf</code></p>

<p>Inside <code>/etc/puppetlabs/puppet/puppet.conf</code> you should see a configuration item
called &lsquo;modulepath&rsquo; that currently has a value of:</p>

<figure class='code'><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=''><span class='line'>modulepath = /etc/puppetlabs/puppet/environments/$environment/modules:/opt/puppet/share/puppet/modules</span></code></pre></td></tr></table></div></figure>


<p>The modulepath itself works like a PATH environment variable in Linux &ndash; it&rsquo;s
a priority-based lookup mechanism that Puppet uses to find modules. Currently,
Puppet will first look in <code>/etc/puppetlabs/puppet/environments/$environment/modules</code>
for a module. If a the module that Puppet was looking for was found, Puppet
will use it and not inspect the second path. If the module was not found at the
FIRST path, it will inspect the second path. Failing to find the module at the
second path results in a compilation error for Puppet. Using this to our
advantage, we can add the path to the monolithic repository checked-out by the
<code>Puppetfile</code> AFTER the path to where all the individual modules are checked-out.
This should look something like this:</p>

<figure class='code'><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=''><span class='line'>modulepath = /etc/puppetlabs/puppet/environments/$environment/modules:/etc/puppetlabs/puppet/environments/$environment/modules/my_big_module_repo:/opt/puppet/share/puppet/modules</span></code></pre></td></tr></table></div></figure>


<p><strong>Note: This assumes all modules are in the root of the monolithic repo. If
they&rsquo;re in a subdirectory, you must adjust accordingly</strong></p>

<p>That&rsquo;s a huge line (and if you&rsquo;re afraid of anything over 80 column-widths then
I&rsquo;m sorry&hellip;and you should probably buy a new monitor&hellip;and the 80s are over),
but the gist is that we&rsquo;re first going to look for modules checked out by R10k,
THEN we&rsquo;re going to look for modules in our monolithic repo, then we&rsquo;re going
to look in Puppet Enterprise&rsquo;s vendored module directory, and finally, like I
said above, we&rsquo;ll fail if we can&rsquo;t find our module. This will allow you to KEEP
using your monolithic repository and also slowly cut modules inside that
monolithic repo over to their own repositories (since when they gain their own
repository, they will be located in a path that COMES before the monolithic
repo, and thus will be given priority).</p>

<h2>Using MCollective to perform R10k synchronizations</h2>

<p>This section is going to be much less specific than the rest because the piece
that does the ACTION is part of <a href="http://forge.puppetlabs.com/zack/r10k">a module for R10k</a>. As of the time
of this writing, this agent is in one state, but that could EASILY change. I
will defer to <a href="http://forge.puppetlabs.com/zack/r10k">the module in question</a> (and specifically its
README file) should you need specifics (or if my module is dated). What I CAN
tell you, however, is that <a href="http://forge.puppetlabs.com/zack/r10k">the R10k module</a> does come with a class
that will setup and configure both an MCollective agent for R10k and also a
helper application that should make doing R10k synchroniations on multiple
Puppet masters much easier than doing them by hand.  First, you&rsquo;ll need to
INSTALL the MCollective agent/application, and you can do that by pulling
down <a href="http://forge.puppetlabs.com/zack/r10k">the module</a> and its dependencies, and classifying all Puppet
masters with R10k enabled by doing the following:</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='puppet'><span class='line'><span class="kn">include</span> <span class="nc">r10k::mcollective</span>
</span></code></pre></td></tr></table></div></figure>


<p>Terribly difficult, huh? With that, both the MCollective agent and application
should be available to MCollective on that node. The way to trigger a
syncronization is to login to an account on a machine that has MCollective
client access (in Puppet Enterprise, this would be any Puppet master that&rsquo;s
allowed the role, and then, specifically, the <code>peadmin</code> user&hellip;so doing a
<code>su - peadmin</code> should afford you access to that user), and perform the following
command:</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='puppet'><span class='line'><span class="err">mco</span> <span class="err">r10k</span> <span class="err">deploy</span>
</span></code></pre></td></tr></table></div></figure>


<p>This is where the README differs a bit, and the reason for that is because Finch
changed the syntax that R10k uses to synchronize and deploy modules to a Master.
The CURRENTLY accepted command (because, knowing Finch, that shit might change)
is <code>r10k deploy environment -p</code>, and the action to the MCollective agent that
EXECUTES that command is the &lsquo;deploy&rsquo; action.  The README refers to the
&lsquo;synchronize&rsquo; action, which executes the <code>r10k synchronize</code> command. This command
MAY STILL WORK, but it&rsquo;s deprecated, and so it&rsquo;s NOT recommended to be used.</p>

<p>Like I said before, this agent is subject to change (mainly do to R10k command
deprecation and maturation), so definitely refer to the README and the code
itself for more information (or
<a href="http://github.com/acidprime/puppet-r10k">file issues and pull requests on the module repo directly</a>).</p>

<h2>Tying R10k to CI workflows</h2>

<p>I spent a year doing some presales work for the Puppet Labs SE team, so I can
hand-wave and tapdance like a motherfucker. I&rsquo;m going to need those skills for
this next section, because if you thought the previous section glossed over the
concepts pretty quickly and without much detail, then this section is going to
feel downright vaporous (is that a word? Fuck it; I&rsquo;m handwaving &ndash; it&rsquo;s
a word). I really debated whether to include the following sections in this
post because I don&rsquo;t really give you much specific information; it&rsquo;s all very
generic and full of &ldquo;ideas&rdquo; (though I do list some testing libraries below that
are helpful if you&rsquo;ve never heard of them). Feel free to abandon ship and skip
to the FINAL section right now if you don&rsquo;t want to hear about &lsquo;ideas&rsquo;.</p>

<p>For the record, I&rsquo;m going to just pick and use the term &ldquo;CI&rdquo; when I&rsquo;m referring
to the process of automating the testing and deployment of, in this case,
Puppet code.  There have definitely been posts arging about which definition is
more appropriate, but, frankly, I&rsquo;m just going to pick a term and go with it,</p>

<p>The issue at hand is that when you talk &ldquo;CI&rdquo; or &ldquo;CD&rdquo; or &ldquo;Continuous (fill_in_the_blank)&rdquo;, you&rsquo;re
talking about a workflow that&rsquo;s tailored to each organization (and sometimes
each DEPARTMENT of an organization). Sometimes places can agree on a specific
tool to assist them with this process (be it Jenkins, Hudson, Bamboo, or
whatever), but beyond that it&rsquo;s anyone&rsquo;s game.</p>

<p>Since we&rsquo;re talking PUPPET code, though, you&rsquo;re restricted to certain tasks
that will show up in any workflow&hellip;and THAT is what I want to talk about here.</p>

<p>To implement some sort of CI workflow means laying down a &lsquo;pipeline&rsquo; that takes a
change of your Puppet code (a new module, a change to an existing module, some
Hiera data updates, whatever) from the developer&rsquo;s/operations engineer&rsquo;s workstation
right into production.  The way we do this with R10k currently is to:</p>

<ul>
<li>Make a change to an individual module</li>
<li>Commit/push those changes to the module&rsquo;s remote repository</li>
<li>Create a test branch of the puppet_repository</li>
<li>Modify the <code>Puppetfile</code> and tie your module&rsquo;s changes to this environment</li>
<li>Commit/push those changes to the puppet_repository</li>
<li>Perform an R10k synchronization</li>
<li>Test</li>
<li>Repeat steps 1-7 as necessary until shit works how you like it</li>
<li>Merge the changes in the test branch of the puppet_repository with the production branch</li>
<li>Perform an R10k synchronization</li>
<li>Watch code changes become active in your production environment</li>
</ul>


<p>Of those steps, there&rsquo;s arguably about 3 unique steps that could be automated:</p>

<ul>
<li>R10k synchronizations</li>
<li>&lsquo;Testing&rsquo; (whatever that means)</li>
<li>Merging the changes in the test branch of the puppet_repository with the production branch</li>
</ul>


<p><strong>NOTE: As we get progressively-more-handwavey (also probably not a word, but fuck it &ndash; let&rsquo;s
be thought leaders and CREATE IT), each one of these steps is going to be more
and more&hellip;generic. For example &ndash; to say &ldquo;test your code&rdquo; is a great idea, but,
seriously, defining how to do that could (and should) be multiple blog posts.</strong></p>

<h3>Laying down the pipeline</h3>

<p>If I were building an automated workflow, the first thing I would do is
setup something like Jenkins and configure it to watch the puppet_repository
that contains the <code>Puppetfile</code> mapping all my modules and versions to Puppet
environments. On changes to this repository, we want Jenkins to perform an R10k
synchronization, run tests, and then, possibly, merge those changes into
production (depending on the quality of your tests and how &lsquo;webscale&rsquo; you think
you are on that day).</p>

<h3>R10k synchronizations</h3>

<p>If you&rsquo;re paying attention, we solved this problem in the previous section with
the R10k MCollective agent. Jenkins should be running on a machine that has the
ability to execute MCollective client commands (such as triggering
<code>mco r10k deploy</code> when necessary).  You&rsquo;ll want to tailor your calls from
Jenkins to only deploy environments it&rsquo;s currently testing (remember in the
puppet_repository that topic branches map to Puppet environments, so this
is a per-branch action) as opposed to deploying ALL environments every time.</p>

<p>Also, if you&rsquo;re buiding a pipeline, you might not want to do R10k
synchronizations on ALL of your Puppet Masters at this point. Why not? Well,
if your testing framework is good enough and has sufficient coverage that
you&rsquo;re COMPLETELY trusting it to determine whether code is acceptable or not,
then this is just the FIRST step &ndash; making the code available to be tested. It&rsquo;s
not passed tests yet, so pushing it out to all of your Puppet masters is a bit
wasteful. You&rsquo;ll probably want to only synchronize with a single master that&rsquo;s
been identified for testing (and a master that has the ability to spin up
fresh nodes, enforce the Puppet code on them, submit those nodes to a battery
of tests, and then tear them down when everything has been completed).</p>

<p>If you&rsquo;re like the VAST majority of Puppet users out there that DON&rsquo;T have a
completely automated testing framework that has such complete coverage that you
trust it to determine whether code changes are acceptable or not, then you&rsquo;re
probably &lsquo;testing&rsquo; changes manually. For these people, you&rsquo;ll probably want to
synchronize code to whichever Puppet master(s) are suitable.</p>

<p>The cool thing about these scenarios is that MCollective is flexible enough
to handle this. MCollective has the ability to filter your nodes based on
things like available MCollective agents, Facter facts, Puppet classes, and
even things like the MD5 hashes of arbitrary files on the filesystem&hellip;so
however you want to restrict synchronization, you can do it with MCollective.</p>

<p>After all of that, the answer here is &ldquo;Use MCollective to do R10k syncs/deploys.&rdquo;</p>

<h3>Testing</h3>

<p>This section needs its own subset of blog posts. There are all kinds of tools
that will allow you to test all sorts of things about your Puppet code (from
basic syntax checking and linting, to integration tests that check for the
presence of resources in the catalog, to acceptance-level tests that check
the end-state of the system to make sure Puppet left it in a state that&rsquo;s
acceptable).  The most common tools for these types of tests are:</p>

<ul>
<li><a href="http://puppet-lint.com">Puppet-lint</a></li>
<li><a href="http://rspec-puppet.com">Rspec-puppet</a></li>
<li><a href="http://github.com/puppetlabs/beaker">Beaker</a></li>
<li><a href="https://github.com/serverspec/serverspec">Serverspec</a></li>
<li>And more&hellip;</li>
</ul>


<p>Unfortunately, the point of this section is NOT to walk you through setting up
one or more of those tools (I&rsquo;d love to write those posts soon&hellip;), but rather
to make you aware of their presence and identify where they fit in our Pipeline.</p>

<p>Once you&rsquo;ve synchronized/deployed code changes to a specific machine (or
subset of machines), the next step is to trigger tests.</p>

<p>Backing up the train a bit, certain kinds of &lsquo;tests&rsquo; should be done WELL in
advance of this step. For example, if code changes don&rsquo;t even pass basic syntax
checking and linting, they shouldn&rsquo;t even MAKE it into your repository. Things
like pre-commit hooks will allow you to trigger syntactical checks and linting
before a commit is allowed. We&rsquo;re assuming you&rsquo;ve already set those up (and
if you&rsquo;ve NOT, then you should probably do that RIGHT NOW).</p>

<p>Rather, in this section, we&rsquo;re talking about doing some basic integration
smoke testing (i.e. running the rspec-puppet tests on all the modules to ensure
that what we EXPECT in the catalog is actually IN the catalog), moving into
acceptance level testing (i.e. spinning up pristine/clean nodes, actually
applying the Puppet code to the nodes, and then running things like Beaker
or Serverspec on the nodes to check the end-state of things like services, open
ports, configuration files, and whatever to ensure that Puppet ACTUALLY left
the system in a workable state), and then returning a &ldquo;PASS&rdquo; or
&ldquo;FAIL&rdquo; response to Jenkins (or whatever is controlling your pipeline).</p>

<p>These tests can be as thorough or as loose as is acceptable to you (obviously,
the goal is to automate ALL of your tests so you don&rsquo;t have to manually check
ANY changes, but that&rsquo;s the nerd-nirvana state where we&rsquo;re all browsing the web
all day), but they should catch the most NOTORIOUS and OBVIOUS things FIRST.
Follow the same rules you did when you got started with Puppet &ndash; catch the
things that are easiest to catch and start building up your cache of &ldquo;Total
Time Saved.&rdquo;</p>

<p>Jenkins needs to be able to trigger these tests from wherever it&rsquo;s running,
so your Jenkins box needs the ability to, say, spin up nodes in ESX, or
locally with something like Vagrant, or even cloud nodes in EC2 or GCE, then
TRIGGER the tests, and finally get a &ldquo;PASS&rdquo; or &ldquo;FAIL&rdquo; response back. The
HARDEST part here, by far, is that you have to define what level of testing
you&rsquo;re going to implement, how you&rsquo;re going to implement it, and devise
the actual process to perform the testing. Like I said before, there are other
blog posts that talk about this (and I hope to tackle this topic in the very
near future), so I&rsquo;ll leave it to them for the moment.</p>

<h3>To merge or not to merge</h3>

<p>The final step for any test code is to determine whether it should be merged
into production or not. Like I said before, if your tests are sufficient and
are adequate at determining whether a change is &lsquo;good&rsquo; or not, then you can
look at automating the process of merging those changes into production and
killing off the test branch (or, NOT merging those changes, and leaving the
branch open for more changes).</p>

<p>Automatically merging is scary for obvious reasons, but it&rsquo;s also a good &lsquo;test&rsquo;
for your test coverage. Committing to a &lsquo;merge upon success&rsquo; workflow takes
trust, and there&rsquo;s absolutely no shame in leaving this step to a human,
to a change review board, or to some out-of-band process.</p>

<h2>Use your illusion</h2>

<p>These are the most common questions I get asked after the initial shock of R10k,
and its workflow, wears off. Understand that I do these posts NOT from a &ldquo;Here&rsquo;s
what you should absolutely be doing!&rdquo; standpoint, but more from a &ldquo;Here&rsquo;s what&rsquo;s
going on out there.&rdquo; vantage. Every time I&rsquo;m called on-site with a customer, I
evaluate:</p>

<ul>
<li>The size and experience level of the team involved</li>
<li>The processes that the team must adhere to</li>
<li>The Puppet experience level of the team</li>
<li>The goals of the team</li>
</ul>


<p>Frankly, after all those observations, sometimes I ABSOLUTELY come to the
conclusion that something like R10k is entirely-too-much process for
not-enough benefit. For those who are a fit, though, we go down the checklists
and tailor the workflow to the environment.</p>

<h2>What more IS there on R10k?</h2>

<p>I do have at least a couple of more posts in me on some specific issues I&rsquo;ve
hit when consulting with companies using R10k, such as:</p>

<ul>
<li>How best to use Hiera and R10k with Puppet &lsquo;environments&rsquo; and internal, long-term &lsquo;environments&rsquo;</li>
<li>Better ideas on &lsquo;what to branch and why&rsquo; with regard to component modules and the puppet_repository</li>
<li>To inherit or not to inherit with Roles</li>
<li>How to name things (note that I work for Puppet Labs, so I&rsquo;m most likely very WRONG with this section)</li>
<li>Other random things I&rsquo;ve noticed&hellip;</li>
</ul>


<p>Also, I apologize if it&rsquo;s been awhile since I&rsquo;ve replied to a couple of
comments. I&rsquo;m booked out 3 months in advance and things are pretty wild at
the moment, but I&rsquo;m REALLY thankful of everyone who cares enough to drop a
note, and I hope I&rsquo;m providing some good info you can actually use! Cheers!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Functional Puppet Workflow Part 3: Dynamic Environments With R10k]]></title>
    <link href="http://garylarizza.com/blog/2014/02/18/puppet-workflow-part-3/"/>
    <updated>2014-02-18T11:48:18-06:00</updated>
    <id>http://garylarizza.com/blog/2014/02/18/puppet-workflow-part-3</id>
    <content type="html"><![CDATA[<p>Workflows are like kickball games: everyone knows the general idea of what&rsquo;s
going on, there&rsquo;s an orderly progression towards an end-goal, nobody wants to
be excluded, and people lose their shit when they get hit in the face by a big
rubber ball.  Okay, so maybe it&rsquo;s not a perfect mapping but you get the idea.</p>

<p>The previous two posts (<a href="http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/">one</a> and <a href="http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/">two</a>) focused on
writing modules, wrapping modules, and classification. While BOTH of these
things are very important in the grand scheme of things, one of the biggest
problems people get hung-up on is how do you iterate upon your modules, and,
more importantly, how do you eventually get these changes pushed into production
in a reasonably orderly fashion?</p>

<p>This post is going to be all over the place. We&rsquo;re gonna cover the idea of
separate environments in Puppet, touch on dynamic environments, and round it
out with that mother-of-a-shell-script-turned-personal-savior, R10k. Hold on
to your shit.</p>

<h2>Puppet Environments</h2>

<p><a href="http://docs.puppetlabs.com/guides/environment.html">Puppet has the concept of &lsquo;environments&rsquo;</a> where you can logically
separate your modules and manifest (read: <code>site.pp</code>) into separate folders
to allow for nodes to get entirely separate bits of code based on which
&lsquo;environment&rsquo; the node belongs to.</p>

<p>Puppet environments are statically set in <code>puppet.conf</code>, but,
<a href="http://bit.ly/puppetgit">as other blog posts have noted</a>, you can do some crafty things in
<code>puppet.conf</code> to give you the solution of having &lsquo;dynamic environments&rsquo;.</p>

<p><strong>NOTE</strong>: The solutions in this post are going to rely on Puppet environments,
however environments aren&rsquo;t without their own shortcomings
<a href="http://projects.puppetlabs.com/issues/12173">namely, this bug on Ruby plugins in Puppet</a>). For testing and
promoting Puppet classes written in the DSL, environments will help you out
greatly. For complete separation of Ruby instances and any plugins to Puppet
written in Ruby, however, you&rsquo;ll need separate masters (which is something that
I won&rsquo;t be covering in this article).</p>

<h2>One step further &ndash; &lsquo;dynamic&rsquo; environments</h2>

<p>Adrien Thebo, hitherto known as &lsquo;Finch&rsquo;, &ndash; who is known for building awesome
things and talking like he&rsquo;s fresh from a Redbull binge &ndash; created the
<a href="http://bit.ly/puppetgit">now-famous blog post on creating dynamic environments in Puppet with git.</a>
That post relied upon a post-commit hook to do all the jiggery-pokery necessary
to checkout the correct branches in the correct places, and thus it had a heavy
reliance upon git.</p>

<p>Truly, the only magic in <code>puppet.conf</code> was the inclusion of &lsquo;$environment&rsquo; in
the <code>modulepath</code> configuration entry on the Puppet master (literally that
string and not the evaluated form of your environment). By doing that, the
Puppet master would replace the string &lsquo;$environment&rsquo; with the environment
of the node checking in and would look to that path for Puppet manifests
and modules. If you use something OTHER than git, it would be up to you to
create a post-receive hook that populated those paths, but you could still
replicate the results (albiet with a little work on your part).</p>

<p>People used this pattern and it worked fairly well. Hell, it STILL works
fairly well, nothing has changed to STOP you from using it. What changed,
however, was the ecosystem around modules, the need for individual module
testing, and the further need to automate this whole goddamn process.</p>

<p>Before we deliver the &lsquo;NEW SOLUTION&rsquo;, let&rsquo;s provide a bit of history and
context.</p>

<h2>Module repositories: the one-to-many problem</h2>

<p>I <a href="http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/">touched on this topic in the first post</a>, but one of the first
problems you encounter when putting your modules in version control is whether
or not to have ONE GIANT REPO with all of your modules, or a repository for
every module you create. In the past we recommended putting every module in
one repository (namely because it was easier, the module sharing landscape
was pretty barren, and teams were smaller). Now, we recommend the opposite
for the following reasons:</p>

<ul>
<li>Individual repos mean individual module development histories</li>
<li>Most VCS solutions don&rsquo;t have per-folder ACLs for a single repositories;
having multiple repos allows per-module security settings.</li>
<li>With the one-repository-per-module solution, modules you pull down from the
Forge (or Github) must be committed to your repo. Having multiple
repositories for each module allow you to keep everything separate</li>
<li>Publishing this module to the Forge (or Github/Stash/whatever) is easier
with separate repos (rather than having to split-out the module later).</li>
</ul>


<p>The problem with having a repository for every Puppet module you create is that
you need a way to map every module with every Puppet master (and, also which
version of every module should be installed in which Puppet environment).</p>

<p><a href="https://github.com/rodjek/librarian-puppet">A project called librarian-puppet</a> sprang up that created the
&lsquo;<code>Puppetfile</code>&rsquo;, a file that would map modules and their versions to a
specific directory. Librarian was awesome,
<a href="http://somethingsinistral.net/blog/scaling-puppet-environment-deployment/">but, as Finch noted in his post</a>, it had some shortcomings
when used in an environment with many and fast-changing modules.
<a href="http://somethingsinistral.net/blog/scaling-puppet-environment-deployment/">His solution, that he documented here,</a>, was the tool we now come
to know as R10k.</p>

<h2>Enter R10k</h2>

<p>R10k is essentially a Ruby project that wraps a bunch of shell commands you
would NORMALLY use to maintain an environment of ever-changing Puppet modules.
Its power is in its ability to use Git branches combined with a <code>Puppetfile</code>
to keep your Puppet environments in-sync. Because of this, R10k is CURRENTLY
restricted to git. There have been rumblings of porting it to Hg or svn, but
I know of no serious attempts at doing this (and if you ARE doing this, may
god have mercy on your soul). Great, so how does it work?</p>

<p>Well, you&rsquo;ll need one main repository SIMPLY for tracking the <code>Puppetfile</code>.
<a href="https://github.com/glarizza/puppet_repository">I&rsquo;ve got one right here,</a> and it only has my <code>Puppetfile</code> and a
<code>site.pp</code> file for classification (should you use it).</p>

<p><strong>NOTE</strong>: The Puppetfile and librarian-puppet-like capabilities under the hood
are going to be doing most of the work here &ndash; this repository is solely so you
can create topic branches with changes to your <code>Puppetfile</code> that will
eventually become dynamically-created Puppet environments.</p>

<p>Let&rsquo;s take a look at the <code>Puppetfile</code> and see what&rsquo;s going on:</p>

<figure class='code'><figcaption><span>Puppetfile </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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">forge</span> <span class="s2">&quot;http://forge.puppetlabs.com&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># Modules from the Puppet Forge</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/stdlib&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/apache&quot;</span><span class="p">,</span> <span class="s2">&quot;0.11.0&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/pe_gem&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/mysql&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/firewall&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/vcsrepo&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/git&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;puppetlabs/inifile&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;zack/r10k&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;gentoo/portage&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;thias/vsftpd&quot;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="c1"># Modules from Github using various references</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;wordpress&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s2">&quot;git://github.com/hunner/puppet-wordpress.git&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;0.4.0&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;property_list_key&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s2">&quot;git://github.com/glarizza/puppet-property_list_key.git&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;952a65d9ea2c5809f4e18f30537925ee45548abc&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">mod</span> <span class="s1">&#39;redis&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s1">&#39;git://github.com/glarizza/puppet-redis&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;feature/debian_support&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>This example lists the syntax for dealing with modules from both the
Forge and Github, as well as pulling specific versions of modules (whether
versions in the case of the Forge, or Github references as tags, branches,
or even specific commits). The syntax is not hard to follow &ndash; just remember
that we&rsquo;re mapping modules and their versions to a set/known environment.</p>

<p>For every topic branch on this repository (containing the <code>Puppetfile</code>), R10k
will in turn create a Puppet environment with the same name. For this reason,
it&rsquo;s convention to rename the &lsquo;master&rsquo; branch to &lsquo;production&rsquo; since that&rsquo;s the
default environment in Puppet (note that renaming branches locally is easy &ndash;
renaming the branch on Github can sometimes be a pain in the ass). You will
also note why it&rsquo;s going to be somewhat hard to map R10k to subversion, for
example, due to the lack of lightweight branching schemes.</p>

<p>To explain any more of R10k reads just as if I were describing its installation,
so let&rsquo;s quit screwing around and actually INSTALL/SETUP the damn thing.</p>

<h2>Setting up R10k</h2>

<p>As I mentioned before, we <a href="https://github.com/glarizza/puppet_repository">have the main repository</a> that will be
used to track the <code>Puppetfile</code>, which in turn will track the modules to
be installed (whether from The Forge, Github, or some internal git repo). Like
any good Puppet component, R10k itself can be setup with a Puppet module.
<a href="http://forge.puppetlabs.com/zack/r10k">The module I&rsquo;ll be using</a> was developed by <a href="https://github.com/acidprime">Zack Smith</a>,
and is pretty simple to get started. Let&rsquo;s download it from the forge first:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 vagrant]# puppet module install zack/r10k
</span><span class='line'>Notice: Preparing to install into /etc/puppetlabs/puppet/modules ...
</span><span class='line'>Notice: Downloading from https://forge.puppetlabs.com ...
</span><span class='line'>Notice: Installing -- do not interrupt ...
</span><span class='line'>/etc/puppetlabs/puppet/modules
</span><span class='line'>└─┬ zack-r10k (v1.0.2)
</span><span class='line'>  ├─┬ gentoo-portage (v2.1.0)
</span><span class='line'>  │ └── puppetlabs-concat (v1.0.1)
</span><span class='line'>  ├── mhuffnagle-make (v0.0.2)
</span><span class='line'>  ├── puppetlabs-gcc (v0.1.0)
</span><span class='line'>  ├── puppetlabs-git (v0.0.3)
</span><span class='line'>  ├── puppetlabs-inifile (v1.0.1)
</span><span class='line'>  ├── puppetlabs-pe_gem (v0.0.1)
</span><span class='line'>  ├── puppetlabs-ruby (v0.1.0)
</span><span class='line'>  └── puppetlabs-vcsrepo (v0.2.0)</span></code></pre></td></tr></table></div></figure>


<p>The module will be installed into the first path in your modulepath, which in
the case above is <code>/etc/puppetlabs/puppet/modules</code>. This modulepath will change
due to the way we&rsquo;re going to setup our dynamic Puppet environments. For this
example, I&rsquo;m going to have environments dynamically generated at
<code>/etc/puppetlabs/puppet/environments</code>, so let&rsquo;s create that directory first:</p>

<figure class='code'><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=''><span class='line'>[root@master1 vagrant]# mkdir -p /etc/puppetlabs/puppet/environments</span></code></pre></td></tr></table></div></figure>


<p>Now, we need to setup R10k on this machine. The module we downloaded will
allow us to do that, but we&rsquo;ll need to create a small Puppet manifest that
will allow us to setup R10k out-of-band from a regular Puppet run (you CAN
continuously-enforce R10k configuration in-band with your regular Puppet
run, but if we&rsquo;re setting up a Puppet master to use R10k to serve out dynamic
environments it&rsquo;s possible to create a chicken-and-egg situation.). Let&rsquo;s
generate a file called <code>r10k_installation.pp</code> in <code>/var/tmp</code> and have it look
like the following:</p>

<figure class='code'><figcaption><span>/var/tmp/r10k_installation.pp </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='puppet'><span class='line'><span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;r10k&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">version</span>           <span class="p">=&gt;</span> <span class="s1">&#39;1.1.3&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">sources</span>           <span class="p">=&gt;</span> <span class="p">{</span>
</span><span class='line'>    <span class="s1">&#39;puppet&#39;</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span><span class='line'>      <span class="s1">&#39;remote&#39;</span>  <span class="p">=&gt;</span> <span class="s1">&#39;https://github.com/glarizza/puppet_repository.git&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;basedir&#39;</span> <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/environments&quot;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;prefix&#39;</span>  <span class="p">=&gt;</span> <span class="ss">false</span><span class="p">,</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="nt">purgedirs</span>         <span class="p">=&gt;</span> <span class="p">[</span><span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/environments&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="nt">manage_modulepath</span> <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">modulepath</span>        <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${::settings::confdir}</span><span class="s2">/environments/</span><span class="se">\$</span><span class="s2">environment/modules:/opt/puppet/share/puppet/modules&quot;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>So what is every section of that declaration doing?</p>

<ul>
<li><strong><code>version =&gt; '1.1.3'</code></strong> sets the version of the R10k gem to install</li>
<li><strong><code>sources =&gt; {...}</code></strong> is a hash of sources that R10k is going to track. For
now it&rsquo;s only our <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>, but you can also track a Hiera
installation too. This hash accepts key/value pairs for configuration settings
that are going to be written to <code>/etc/r10k.yaml</code>, which is R10k&rsquo;s main
configuration file. The keys in-use are <code>remote</code>, which is the path to the
repository to-be-checked-out by R10k, <code>basedir</code>, which is the path on-disk
to where dynamic environments are to be created (we&rsquo;re using the
<code>$::settings::confdir</code> variable which maps to the Puppet master&rsquo;s configuration
directory, or <code>/etc/puppetlabs/puppet</code>), and <code>prefix</code> which is a boolean
to determine whether to use R10k&rsquo;s source-prefixing feature. <strong>NOTE: the <code>false</code>
value is a BOOLEAN value, and thus SHOULD NOT BE QUOTED. Quoting it turns it
into a string, which matches as a boolean TRUE value. Don&rsquo;t quote <code>false</code> &ndash;
that&rsquo;s bad, mmkay.</strong></li>
<li><strong><code>purgedirs=&gt; ["${::settings::confdir}/environments"]</code></strong> is configuring R10k
to implement purging on the environments directory (so any folders that R10k
doesn&rsquo;t create it will delete). This configuration MAY be moot with newer
versions of R10k as I believe it implements this behavior by default.</li>
<li><strong><code>manage_modulepath =&gt; true</code></strong> will ensure that this module sets the <code>modulepath</code>
configuration item in <code>/etc/puppetlabs/puppet/puppet.conf</code></li>
<li><strong><code>modulepath =&gt; ...</code></strong> sets the <code>modulepath</code> value to be dropped into
<code>/etc/puppetlabs/puppet/puppet.conf</code>. Note that we are interpolating variables
(<code>$::settings::confdir</code> again), AND inserting the LITERAL string of <code>$environment</code>
into the <code>modulepath</code> &ndash; this is because Puppet will replace <code>$environment</code> with
the value of the agent&rsquo;s environment at catalog compilation.</li>
</ul>


<p><strong>JUST IN CASE YOU MISSED IT: Don&rsquo;t quote the <code>false</code> value for the prefix setting
in the <code>sources</code> block. That is all.</strong></p>

<p>Okay, we have our one-time Puppet manifest, and now the only thing left to do
is to run it:</p>

<figure class='code'><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=''><span class='line'>[root@master1 tmp]# puppet apply /var/tmp/r10k_installation.pp
</span><span class='line'>Notice: Compiled catalog for master1 in environment production in 2.05 seconds
</span><span class='line'>Notice: /Stage[main]/R10k::Config/File[r10k.yaml]/ensure: defined content as '{md5}0b619d5148ea493e2d6a5bb205727f0c'
</span><span class='line'>Notice: /Stage[main]/R10k::Config/Ini_setting[R10k Modulepath]/value: value changed '/etc/puppetlabs/puppet/modules:/opt/puppet/share/puppet/modules' to '/etc/puppetlabs/puppet/environments/$environment/modules:/opt/puppet/share/puppet/modules'
</span><span class='line'>Notice: /Package[r10k]/ensure: created
</span><span class='line'>Notice: /Stage[main]/R10k::Install::Pe_gem/File[/usr/bin/r10k]/ensure: created
</span><span class='line'>Notice: Finished catalog run in 10.55 seconds</span></code></pre></td></tr></table></div></figure>


<p>At this point, it goes without saying that <code>git</code> needs to be installed, but
if you&rsquo;re firing up a new VM that DOESN&rsquo;T have <code>git</code>, then R10k is going to
spit out an awesome error &ndash; so ensure that <code>git</code> is installed.  After that,
let&rsquo;s synchronize R10k with the <code>r10k deploy environment -pv</code> command (<code>-p</code>
for <code>Puppetfile</code> synchronization and <code>-v</code> for verbose mode):</p>

<figure class='code'><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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 puppet]# r10k deploy environment -pv
</span><span class='line'>[R10K::Task::Deployment::DeployEnvironments - INFO] Loading environments from all sources
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment production
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying make into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying concat into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ruby into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying make into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying concat into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying ruby into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment master
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying redis into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/master/modules
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment development
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying property_list_key into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying wordpress into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying vsftpd into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying mysql into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppetlabs/puppet/environments/development/modules
</span><span class='line'>[R10K::Task::Deployment::PurgeEnvironments - INFO] Purging stale environments from /etc/puppetlabs/puppet/environments</span></code></pre></td></tr></table></div></figure>


<p>I ran this first synchronization with verbose mode so you can see exactly what&rsquo;s
getting copied where. Futher synchronizations don&rsquo;t have to be in verbose mode,
but it&rsquo;s good for debugging. After all of that, we have an
<code>/etc/puppetlabs/puppet/environments</code> folder containing our dynamic Puppet
environments based off of the branches of the <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>:</p>

<figure class='code'><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=''><span class='line'>[root@master1 puppet]# ls -lah /etc/puppetlabs/puppet/environments/
</span><span class='line'>total 20K
</span><span class='line'>drwxr-xr-x 5 root root 4.0K Feb 19 11:44 .
</span><span class='line'>drwxr-xr-x 7 root root 4.0K Feb 19 11:25 ..
</span><span class='line'>drwxr-xr-x 4 root root 4.0K Feb 19 11:44 development
</span><span class='line'>drwxr-xr-x 5 root root 4.0K Feb 19 11:43 master
</span><span class='line'>drwxr-xr-x 5 root root 4.0K Feb 19 11:42 production
</span><span class='line'>
</span><span class='line'>[root@master1 puppet]# cd /etc/puppetlabs/puppet/environments/production/
</span><span class='line'>[root@master1 production]# git branch -a
</span><span class='line'>  master
</span><span class='line'>* production
</span><span class='line'>  remotes/origin/HEAD -&gt; origin/master
</span><span class='line'>  remotes/origin/development
</span><span class='line'>  remotes/origin/master
</span><span class='line'>  remotes/origin/production</span></code></pre></td></tr></table></div></figure>


<p>As you can see (at the time of this writing), my <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>
has three main branches: <code>development</code>, <code>master</code>, and <code>production</code>, and so
R10k created three Puppet environments matching those names. It&rsquo;s somewhat
of a convention to rename the master branch to <code>production</code>, but in this
case I left it alone to demonstrate how this works.</p>

<p><strong>ONE OTHER BIG GOTCHA</strong>: R10k does NOT resolve dependencies, and
so it is UP TO YOU to track them in your Puppetfile.  Check this out:</p>

<figure class='code'><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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 production]# puppet module list
</span><span class='line'>Warning: Module 'puppetlabs-firewall' (v1.0.0) fails to meet some dependencies:
</span><span class='line'>  'puppetlabs-puppet_enterprise' (v3.1.0) requires 'puppetlabs-firewall' (v0.3.x)
</span><span class='line'>Warning: Module 'puppetlabs-stdlib' (v4.1.0) fails to meet some dependencies:
</span><span class='line'>  'puppetlabs-pe_accounts' (v2.0.1) requires 'puppetlabs-stdlib' (v3.2.x)
</span><span class='line'>  'puppetlabs-pe_mcollective' (v0.1.14) requires 'puppetlabs-stdlib' (v3.2.x)
</span><span class='line'>  'puppetlabs-puppet_enterprise' (v3.1.0) requires 'puppetlabs-stdlib' (v3.2.x)
</span><span class='line'>  'puppetlabs-request_manager' (v0.0.10) requires 'puppetlabs-stdlib' (v3.2.x)
</span><span class='line'>Warning: Missing dependency 'cprice404-inifile':
</span><span class='line'>  'puppetlabs-pe_puppetdb' (v0.0.11) requires 'cprice404-inifile' (&gt;=0.9.0)
</span><span class='line'>  'puppetlabs-puppet_enterprise' (v3.1.0) requires 'cprice404-inifile' (v0.10.x)
</span><span class='line'>  'puppetlabs-puppetdb' (v1.5.1) requires 'cprice404-inifile' (&gt;= 0.10.3)
</span><span class='line'>Warning: Missing dependency 'puppetlabs-concat':
</span><span class='line'>  'puppetlabs-apache' (v0.11.0) requires 'puppetlabs-concat' (&gt;= 1.0.0)
</span><span class='line'>  'gentoo-portage' (v2.1.0) requires 'puppetlabs-concat' (v1.0.x)
</span><span class='line'>Warning: Missing dependency 'puppetlabs-gcc':
</span><span class='line'>  'zack-r10k' (v1.0.2) requires 'puppetlabs-gcc' (&gt;= 0.0.3)
</span><span class='line'>/etc/puppetlabs/puppet/environments/production/modules
</span><span class='line'>├── gentoo-portage (v2.1.0)
</span><span class='line'>├── mhuffnagle-make (v0.0.2)
</span><span class='line'>├── property_list_key (???)
</span><span class='line'>├── puppetlabs-apache (v0.11.0)
</span><span class='line'>├── puppetlabs-firewall (v1.0.0)  invalid
</span><span class='line'>├── puppetlabs-git (v0.0.3)
</span><span class='line'>├── puppetlabs-inifile (v1.0.1)
</span><span class='line'>├── puppetlabs-mysql (v2.2.1)
</span><span class='line'>├── puppetlabs-pe_gem (v0.0.1)
</span><span class='line'>├── puppetlabs-ruby (v0.1.0)
</span><span class='line'>├── puppetlabs-stdlib (v4.1.0)  invalid
</span><span class='line'>├── puppetlabs-vcsrepo (v0.2.0)
</span><span class='line'>├── redis (???)
</span><span class='line'>├── ripienaar-concat (v0.2.0)
</span><span class='line'>├── thias-vsftpd (v0.2.0)
</span><span class='line'>├── wordpress (???)
</span><span class='line'>└── zack-r10k (v1.0.2)
</span><span class='line'>/opt/puppet/share/puppet/modules
</span><span class='line'>├── cprice404-inifile (v0.10.3)
</span><span class='line'>├── puppetlabs-apt (v1.1.0)
</span><span class='line'>├── puppetlabs-auth_conf (v0.1.7)
</span><span class='line'>├── puppetlabs-firewall (v0.3.0)  invalid
</span><span class='line'>├── puppetlabs-java_ks (v1.1.0)
</span><span class='line'>├── puppetlabs-pe_accounts (v2.0.1)
</span><span class='line'>├── puppetlabs-pe_common (v0.1.0)
</span><span class='line'>├── puppetlabs-pe_mcollective (v0.1.14)
</span><span class='line'>├── puppetlabs-pe_postgresql (v0.0.5)
</span><span class='line'>├── puppetlabs-pe_puppetdb (v0.0.11)
</span><span class='line'>├── puppetlabs-postgresql (v2.5.0)
</span><span class='line'>├── puppetlabs-puppet_enterprise (v3.1.0)
</span><span class='line'>├── puppetlabs-puppetdb (v1.5.1)
</span><span class='line'>├── puppetlabs-reboot (v0.1.2)
</span><span class='line'>├── puppetlabs-request_manager (v0.0.10)
</span><span class='line'>├── puppetlabs-stdlib (v3.2.0)  invalid
</span><span class='line'>└── ripienaar-concat (v0.2.0)</span></code></pre></td></tr></table></div></figure>


<p>I&rsquo;ve installed Puppet Enterprise 3.1.0, and so <code>/opt/puppet/share/puppet/modules</code>
reflects the state of the Puppet Enterprise (also known as &lsquo;PE&rsquo;) modules at that
time. You can see that there are some conflicts because certain modules require
certain versions of other modules. This is currently the nature of the beast
with regard to Puppet modules. Some of these errors are loud and incidental
(i.e. someone set a dependency on a version and forgot to update it), some
are due to namespace changes (i.e. <code>cfprice-inifile</code> being ported over to
<code>puppetlabs-inifile</code>), and so on. Basically, ensure that you handle the
dependencies you care about inside the <code>Puppetfile</code> as R10k won&rsquo;t do it for you.</p>

<p>There &ndash; we&rsquo;ve done it!  We&rsquo;ve configured R10k!  Now how the hell do you use it?</p>

<h2>R10k demonstration &ndash; from module iteration to environment iteration</h2>

<p>Let&rsquo;s take the environment we&rsquo;ve setup in the previous steps and walk you
through adding a new module to your production environment, iterating upon
that module, pushing the changes to that module, pushing the changes to a
Puppet environment, and then promoting those changes to production.</p>

<p><strong>NOTES ON THE SETUP OF THIS DEMO:</strong></p>

<ul>
<li>In this demonstration, classification method is going to be left to the user
(i.e. it&rsquo;s not a part of the magic).  So, when I tell you to classify your
node with a specific class, I don&rsquo;t care if you use the Puppet Enterprise
Console, <code>site.pp</code>, or any other manner.</li>
<li>I&rsquo;m using Github for my repositories so that you folk watching and playing
along at home can have something to follow. Feel free to substitute Github
for something like Atlassian Stash/Bitbucket, internal repos, or whatever.</li>
</ul>


<h3>Add the module to an environment</h3>

<p>The module we&rsquo;ll be working with, <a href="https://github.com/glarizza/puppet-notifyme">a simple module called &lsquo;notifyme&rsquo;</a>,
will notify a message that will help us track the module&rsquo;s process through all
phases of iteration.</p>

<p>The first thing we need to do is to add the module to an environment, so let&rsquo;s
dynamically create a NEW environment by creating a new topic branch and pushing
it up to <a href="https://github.com/glarizza/puppet_repository">the main puppet repo</a>. I will perform this step on my laptop
and outside of the VM I&rsquo;m using to test R10k:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet_repository)▷ git branch
</span><span class='line'>  master
</span><span class='line'>* production
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git checkout -b notifyme
</span><span class='line'>Switched to a new branch 'notifyme'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ vim Puppetfile
</span><span class='line'>
</span><span class='line'># Perform the changes to Puppetfile here
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git add Puppetfile
</span><span class='line'>└(~/src/puppet_repository)▷ git commit
</span><span class='line'>[notifyme 5239538] Add the 'notifyme' module
</span><span class='line'> 1 file changed, 3 insertions(+)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git push origin notifyme:notifyme
</span><span class='line'>Counting objects: 5, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (3/3), 348 bytes, done.
</span><span class='line'>Total 3 (delta 1), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet_repository.git
</span><span class='line'> * [new branch]      notifyme -&gt; notifyme</span></code></pre></td></tr></table></div></figure>


<p>The contents I added to my <code>Puppetfile</code> look like this:</p>

<figure class='code'><figcaption><span>Puppetfile </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='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;notifyme&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s2">&quot;git://github.com/glarizza/puppet-notifyme.git&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Perform an R10k synchronization</h3>

<p>To pull the new dynamic environment down to the Puppet master, do another
R10k synchronization with <code>r10k deploy environment -pv</code>:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 production]# r10k deploy environment -pv
</span><span class='line'>[R10K::Task::Deployment::DeployEnvironments - INFO] Loading environments from all sources
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment production
</span><span class='line'>&lt;snip for brevity&gt;
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment notifyme
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/notifyme/modules
</span><span class='line'>&lt;more snipping&gt;</span></code></pre></td></tr></table></div></figure>


<p>I only included the relevant messages, but you can see that it pulled in a new
environment called &lsquo;notifyme&rsquo; that ALSO pulled in a module called &lsquo;notifyme&rsquo;</p>

<h3>Rename the branch to avoid confusion</h3>

<p>Suddenly I realize that this may get confusing having both an environment called
&lsquo;notifyme&rsquo; with a module/class called &lsquo;notifyme&rsquo;. No worries, how about we rename
that branch?</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet_repository)▷ git branch -m notifyme garysawesomeenvironment
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git push origin :notifyme
</span><span class='line'>To https://github.com/glarizza/puppet_repository.git
</span><span class='line'> - [deleted]         notifyme
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git push origin garysawesomeenvironment:garysawesomeenvironment
</span><span class='line'>Counting objects: 5, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (3/3), 348 bytes, done.
</span><span class='line'>Total 3 (delta 1), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet_repository.git
</span><span class='line'> * [new branch]      garysawesomeenvironment -&gt; garysawesomeenvironment</span></code></pre></td></tr></table></div></figure>


<p>That bit of <code>git</code> renamed the &lsquo;notifyme&rsquo; branch to &lsquo;garysawesomeenvironment&rsquo;.
The next git command is a bit tricky &ndash; when you <code>git push</code> to a remote, it&rsquo;s
supposed to be:</p>

<p><strong><code>git push name_of_origin local_branch_name:remote_branch_name</code></strong></p>

<p>In our case, the name of our origin is LITERALLY &lsquo;origin&rsquo;, but we actually want
to DELETE a remote branch. The way to delete a local branch is with
<code>git branch -d branch_name</code>, but the way to delete a REMOTE branch is to push
<strong>NOTHING</strong> to it.  So consider the following command:</p>

<p><strong><code>git push origin :notifyme</code></strong></p>

<p>We&rsquo;re pushing to the origin named &lsquo;origin&rsquo;, but providing NO local branch name
and pushing that bit of nothing to the remote branch of &lsquo;notifyme&rsquo;. This
kills (deletes) the remote branch.</p>

<p>Finally, we push to our origin named &lsquo;origin&rsquo; again and push the contents of
the local branch &lsquo;garysawesomeenvironment&rsquo; to the remote branch of
&lsquo;garysawesomeenvironment&rsquo; which in turn CREATES that branch if it doesn&rsquo;t exist.
Whew.  Let&rsquo;s run another damn synchronization:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 production]# `r10k deploy environment -pv`
</span><span class='line'>[R10K::Task::Deployment::DeployEnvironments - INFO] Loading environments from all sources
</span><span class='line'>&lt;more snippage&gt;
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment garysawesomeenvironment
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>[R10K::Task::Module::Sync - INFO] Deploying notifyme into /etc/puppetlabs/puppet/environments/garysawesomeenvironment/modules
</span><span class='line'>&lt;more of that snipping shit&gt;
</span><span class='line'>R10K::Task::Deployment::PurgeEnvironments - INFO] Purging stale environments from /etc/puppetlabs/puppet/environments</span></code></pre></td></tr></table></div></figure>


<p>Cool, let&rsquo;s check out our <code>environments</code> folder on our VM:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 production]# ls -lah /etc/puppetlabs/puppet/environments/
</span><span class='line'>total 24K
</span><span class='line'>drwxr-xr-x 6 root root 4.0K Feb 19 13:34 .
</span><span class='line'>drwxr-xr-x 7 root root 4.0K Feb 19 12:09 ..
</span><span class='line'>drwxr-xr-x 4 root root 4.0K Feb 19 11:44 development
</span><span class='line'>drwxr-xr-x 5 root root 4.0K Feb 19 13:33 garysawesomeenvironment
</span><span class='line'>drwxr-xr-x 5 root root 4.0K Feb 19 11:43 master
</span><span class='line'>drwxr-xr-x 5 root root 4.0K Feb 19 11:42 production
</span><span class='line'>
</span><span class='line'>[root@master1 production]# cd /etc/puppetlabs/puppet/environments/garysawesomeenvironment/
</span><span class='line'>
</span><span class='line'>[root@master1 garysawesomeenvironment]# git branch
</span><span class='line'>* garysawesomeenvironment
</span><span class='line'>  master</span></code></pre></td></tr></table></div></figure>


<h3>Run Puppet to test the new environment</h3>

<p>Perfect!  Now classify your node to include the &lsquo;notifyme&rsquo; class, and let&rsquo;s run
Puppet to see what we get when we try to join the environment called
&lsquo;garysawesomeenvronment&rsquo;:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# puppet agent -t --environment garysawesomeenvironment
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snipping facts loading for brevity&gt;
</span><span class='line'>Info: Caching catalog for master1
</span><span class='line'>Info: Applying configuration version '1392845863'
</span><span class='line'>Notice: This is the notifyme module and its master branch
</span><span class='line'>Notice: /Stage[main]/Notifyme/Notify[This is the notifyme module and its master branch]/message: defined 'message' as 'This is the notifyme module and its master branch'
</span><span class='line'>Notice: Finished catalog run in 11.10 seconds</span></code></pre></td></tr></table></div></figure>


<p>Cool!  Now let&rsquo;s try to run Puppet with another environment, say &lsquo;production&rsquo;:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# puppet agent -t --environment production
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snipping facts loading for brevity&gt;
</span><span class='line'>Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Could not find class notifyme for master1 on node master1
</span><span class='line'>Warning: Not using cache on failed catalog
</span><span class='line'>Error: Could not retrieve catalog; skipping run</span></code></pre></td></tr></table></div></figure>


<p>We get an error because that module hasn&rsquo;t been loaded by R10k for that
environment.</p>

<h3>Tie a module version to an environment</h3>

<p>Okay, so we added a module to a new environment, but what if we want to test
out a specific commit, branch, or tag of a module and test it in this new
environment? This is frequently what you&rsquo;ll be doing &ndash; making a change to
an existing module, pushing your change to a topic branch of that module&rsquo;s
repository, tying it to an environment (or creating a new environment by
branching the main Puppet repository), and then testing the change.</p>

<p>Let&rsquo;s go back to my &lsquo;notifyme&rsquo; module that I&rsquo;ve cloned to my laptop and push
a change to a BRANCH of that module&rsquo;s Github repository:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet-notifyme)▷ git branch
</span><span class='line'>* master
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git checkout -b change_the_message
</span><span class='line'>Switched to a new branch 'change_the_message'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ vim manifests/init.pp
</span><span class='line'>## Make changes to the notify message
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git add manifests/init.pp
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git commit
</span><span class='line'>[change_the_message bc3975b] Change the Message
</span><span class='line'> 1 file changed, 1 insertion(+), 1 deletion(-)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git push origin change_the_message:change_the_message
</span><span class='line'>Counting objects: 7, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (4/4), 448 bytes, done.
</span><span class='line'>Total 4 (delta 0), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet-notifyme.git
</span><span class='line'> * [new branch]      change_the_message -&gt; change_the_message
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git branch -a
</span><span class='line'>* change_the_message
</span><span class='line'>  master
</span><span class='line'>  remotes/origin/change_the_message
</span><span class='line'>  remotes/origin/master
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git log
</span><span class='line'>commit bc3975bb5c75ada86bfc2c45db628b5a156f85ce
</span><span class='line'>Author: Gary Larizza &lt;gary@puppetlabs.com&gt;
</span><span class='line'>Date:   Wed Feb 19 13:55:26 2014 -0800
</span><span class='line'>
</span><span class='line'>    Change the Message
</span><span class='line'>
</span><span class='line'>    This commit changes the message to test my workflow.</span></code></pre></td></tr></table></div></figure>


<p>What I&rsquo;m showing you is the workflow that creates a new local branch called
&lsquo;change_the_message&rsquo; to the notifyme module, changes the message in my notify
resource, commits the change, and pushes the changes to a remote branch ALSO
called &lsquo;change_the_message&rsquo;.</p>

<p>Because I created a topic branch, I can provide that branch name in the
<code>Puppetfile</code> located in the &lsquo;garysawesomeenvironment&rsquo; branch of the
<a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>. THAT is the piece that ties together the specific
version of the module with the Puppet environment we want on the Puppet master.
Here&rsquo;s that change:</p>

<figure class='code'><figcaption><span>Puppetfile </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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;notifyme&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s2">&quot;git://github.com/glarizza/puppet-notifyme.git&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;change_the_message&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Again, that change gets put into the &lsquo;garysawesomeenvironment&rsquo; branch of the
<a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a> and pushed up to the remote:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet_repository)▷ vim Puppetfile
</span><span class='line'>## Make changes
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git add Puppetfile
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git commit
</span><span class='line'>[garysawesomeenvironment 89b139c] Update garysawesomeenvironment
</span><span class='line'> 1 file changed, 2 insertions(+), 1 deletion(-)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git push origin garysawesomeenvironment:garysawesomeenvironment
</span><span class='line'>Counting objects: 5, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (3/3), 411 bytes, done.
</span><span class='line'>Total 3 (delta 1), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet_repository.git
</span><span class='line'>   5239538..89b139c  garysawesomeenvironment -&gt; garysawesomeenvironment
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git log -p
</span><span class='line'>commit 89b139c8c2faa888a402b98ea76e4ca138b3463d
</span><span class='line'>Author: Gary Larizza &lt;gary@puppetlabs.com&gt;
</span><span class='line'>Date:   Wed Feb 19 14:04:18 2014 -0800
</span><span class='line'>
</span><span class='line'>    Update garysawesomeenvironment
</span><span class='line'>
</span><span class='line'>    Tie this environment to the 'change_the_message' branch of my notifyme module.
</span><span class='line'>
</span><span class='line'>diff --git a/Puppetfile b/Puppetfile
</span><span class='line'>index 5e5d091..27fc06e 100644
</span><span class='line'>--- a/Puppetfile
</span><span class='line'>+++ b/Puppetfile
</span><span class='line'>@@ -31,4 +31,5 @@ mod 'redis',
</span><span class='line'>   :ref =&gt; 'feature/debian_support'
</span><span class='line'>
</span><span class='line'> mod "notifyme",
</span><span class='line'>-  :git =&gt; "git://github.com/glarizza/puppet-notifyme.git"
</span><span class='line'>+  :git =&gt; "git://github.com/glarizza/puppet-notifyme.git",
</span><span class='line'>+  :ref =&gt; 'change_the_message'</span></code></pre></td></tr></table></div></figure>


<p>Now let&rsquo;s synchronize again!!</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# `r10k deploy environment -pv`
</span><span class='line'>[R10K::Task::Deployment::DeployEnvironments - INFO] Loading environments from all sources
</span><span class='line'>&lt;snip&gt;
</span><span class='line'>[R10K::Task::Environment::Deploy - NOTICE] Deploying environment garysawesomeenvironment
</span><span class='line'>[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
</span><span class='line'>&lt;snip&gt;</span></code></pre></td></tr></table></div></figure>


<p>Cool, let&rsquo;s check our work on the VM:</p>

<figure class='code'><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=''><span class='line'>[root@master1 garysawesomeenvironment]# pwd
</span><span class='line'>/etc/puppetlabs/puppet/environments/garysawesomeenvironment
</span><span class='line'>[root@master1 garysawesomeenvironment]# git branch
</span><span class='line'>* garysawesomeenvironment
</span><span class='line'>  master</span></code></pre></td></tr></table></div></figure>


<p>And finally, let&rsquo;s run Puppet:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>root@master1 garysawesomeenvironment]# puppet agent -t --environment garysawesomeenvironment
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snip fact loading&gt;
</span><span class='line'>Info: Caching catalog for master1
</span><span class='line'>Info: Applying configuration version '1392847743'
</span><span class='line'>Notice: This is the changed message in the change_the_message branch
</span><span class='line'>Notice: /Stage[main]/Notifyme/Notify[This is the changed message in the change_the_message branch]/message: defined 'message' as 'This is the changed message in the change_the_message branch'
</span><span class='line'>Notice: Finished catalog run in 12.10 seconds</span></code></pre></td></tr></table></div></figure>


<p>TADA! We&rsquo;ve successfully tied a specific version of a module to a specific
dynamic environment, deployed it to a master, and tested it out! Smell that?
That&rsquo;s the smell of awesome. Or Jeff in the next cubicle eating a burrito.
Either way, I like it.</p>

<h3>Merge your changes with master/production</h3>

<p>It&rsquo;s green &ndash; fuck it; ship it! NOW you&rsquo;re speaking &lsquo;agile&rsquo;! Assuming everything
went according to plan, let&rsquo;s merge our changes in with the production
environment and synchronize.  This is up to your company&rsquo;s workflow docs
(whether you use pull requests, a merge master, or poke Patrick and tell
him to tell Andy to merge in your change). I&rsquo;m using git and Github, so
let&rsquo;s merge.</p>

<p>First, do the Module:</p>

<figure class='code'><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=''><span class='line'>└(~/src/puppet-notifyme)▷ git checkout master
</span><span class='line'>Switched to branch 'master'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git merge change_the_message
</span><span class='line'>Updating d44a790..bc3975b
</span><span class='line'>Fast-forward
</span><span class='line'> manifests/init.pp | 2 +-
</span><span class='line'> 1 file changed, 1 insertion(+), 1 deletion(-)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git push origin master:master
</span><span class='line'>Total 0 (delta 0), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet-notifyme.git
</span><span class='line'>   d44a790..bc3975b  master -&gt; master
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ cat manifests/init.pp
</span><span class='line'>class notifyme {
</span><span class='line'>  notify { "This is the changed message in the change_the_message branch": }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>So now we have an issue, and that issue is that the production environment
has YET to have the &lsquo;notifyme&rsquo; module added to it.  If we merge the contents
of the &lsquo;garysawesomeenvironment&rsquo; branch with the &lsquo;production&rsquo; branch of the
<a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>, then we&rsquo;re going to be pointing at the
&lsquo;change_the_message&rsquo; branch of the &lsquo;notifyme&rsquo; module (because that was our last
commit).</p>

<p>Because of this, I can&rsquo;t do a straight merge, can I? For posterity&rsquo;s sake (in
the event that someone in the future wants to look for that branch on my Github
repo), I&rsquo;m going to keep that branch alive. In a production environment, I
most likely would NOT have additional branches open for all my component modules
as that would get pretty annoying/confusing. Understand that this is a one-off
case because I&rsquo;m doing a demo. BECAUSE of this, I&rsquo;m going to modify the
<code>Puppetfile</code> in the &lsquo;production&rsquo; branch of the <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet_repository)▷ git checkout production
</span><span class='line'>Switched to branch 'production'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ vim Puppetfile
</span><span class='line'>## Make changes here
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git add Puppetfile
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git commit
</span><span class='line'>[production a74f269] Add notifyme module to Production environment
</span><span class='line'> 1 file changed, 4 insertions(+)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git push origin production:production
</span><span class='line'>Counting objects: 5, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (3/3), 362 bytes, done.
</span><span class='line'>Total 3 (delta 1), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet_repository.git
</span><span class='line'>   5ecefc8..a74f269  production -&gt; production
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git log -p
</span><span class='line'>commit a74f26975102f3786eedddace89bda086162d801
</span><span class='line'>Author: Gary Larizza &lt;gary@puppetlabs.com&gt;
</span><span class='line'>Date:   Wed Feb 19 14:24:05 2014 -0800
</span><span class='line'>
</span><span class='line'>    Add notifyme module to Production environment
</span><span class='line'>
</span><span class='line'>diff --git a/Puppetfile b/Puppetfile
</span><span class='line'>index 0b1da68..9168a81 100644
</span><span class='line'>--- a/Puppetfile
</span><span class='line'>+++ b/Puppetfile
</span><span class='line'>@@ -29,3 +29,7 @@ mod "property_list_key",
</span><span class='line'> mod 'redis',
</span><span class='line'>   :git =&gt; 'git://github.com/glarizza/puppet-redis',
</span><span class='line'>   :ref =&gt; 'feature/debian_support'
</span><span class='line'>+
</span><span class='line'>+mod 'notifyme',
</span><span class='line'>+  :git =&gt; 'git://github.com/glarizza/puppet-notifyme'
</span><span class='line'>+</span></code></pre></td></tr></table></div></figure>


<p>Alright, we&rsquo;ve updated the production environment, now synchronize again
(I&rsquo;ll spare you and do it WITHOUT verbose mode):</p>

<figure class='code'><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=''><span class='line'>[root@master1 garysawesomeenvironment]# r10k deploy environment -p</span></code></pre></td></tr></table></div></figure>


<p>Okay, now run Puppet with the PRODUCTION environment:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# puppet agent -t --environment production
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snipping fact loading&gt;
</span><span class='line'>Info: Caching catalog for master1
</span><span class='line'>Info: Applying configuration version '1392848588'
</span><span class='line'>Notice: This is the changed message in the change_the_message branch
</span><span class='line'>Notice: /Stage[main]/Notifyme/Notify[This is the changed message in the change_the_message branch]/message: defined 'message' as 'This is the changed message in the change_the_message branch'
</span><span class='line'>Notice: Finished catalog run in 12.66 seconds</span></code></pre></td></tr></table></div></figure>


<p>Beautiful, we&rsquo;re synchronized!!!</p>

<h3>Making a change to an EXISTING module in an environment</h3>

<p>Okay, so we saw previously how to add a NEW module to an environment, but what
if we already HAVE a module in an environment and we want to make an update/change
to it?  Well, it&rsquo;s largely the same process:</p>

<ul>
<li>Cut a branch to the module</li>
<li>Commit your code and push it up to the module&rsquo;s repo</li>
<li>Cut a branch to the <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a></li>
<li>Push that branch up to the <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a></li>
<li>Perform an R10k synchronization to sync the environments</li>
<li>Test your changes</li>
<li>Merge the changes with the master branch of the module</li>
<li>DONE!</li>
</ul>


<p>Let&rsquo;s go back and change that notify message again, shall we?</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet-notifyme)▷ git checkout -b 'another_change'
</span><span class='line'>Switched to a new branch 'another_change'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ vim manifests/init.pp
</span><span class='line'>## Make changes to the message
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git add manifests/init.pp
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git commit
</span><span class='line'>[another_change 608166e] Change the message that already exists!
</span><span class='line'> 1 file changed, 1 insertion(+), 1 deletion(-)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git push origin another_change:another_change
</span><span class='line'>Counting objects: 7, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (4/4), 426 bytes, done.
</span><span class='line'>Total 4 (delta 0), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet-notifyme.git
</span><span class='line'> * [new branch]      another_change -&gt; another_change</span></code></pre></td></tr></table></div></figure>


<p>Okay, let&rsquo;s re-use &lsquo;garysawesomeenvironment&rsquo; because I like the name, but
tie it to the new &lsquo;another_change&rsquo; branch of the &lsquo;notifyme&rsquo; module:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>└(~/src/puppet_repository)▷ git checkout garysawesomeenvironment
</span><span class='line'>Switched to branch 'garysawesomeenvironment'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ vim Puppetfile
</span><span class='line'>## Make change to Puppetfile to tie it to 'another_change' branch
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git add Puppetfile
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git commit
</span><span class='line'>[garysawesomeenvironment ce84a30] Tie garysawesomeenvironment to 'another_change'
</span><span class='line'> 1 file changed, 1 insertion(+), 1 deletion(-)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet_repository)▷ git push origin garysawesomeenvironment:garysawesomeenvironment
</span><span class='line'>Counting objects: 5, done.
</span><span class='line'>Delta compression using up to 4 threads.
</span><span class='line'>Compressing objects: 100% (3/3), done.
</span><span class='line'>Writing objects: 100% (3/3), 386 bytes, done.
</span><span class='line'>Total 3 (delta 1), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet_repository.git
</span><span class='line'>   89b139c..ce84a30  garysawesomeenvironment -&gt; garysawesomeenvironment</span></code></pre></td></tr></table></div></figure>


<p>The <code>Puppetfile</code> for that branch now has an entry for the &lsquo;notifyme&rsquo; module
that looks like this:</p>

<figure class='code'><figcaption><span>Puppetfile </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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;notifyme&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s2">&quot;git://github.com/glarizza/puppet-notifyme.git&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;another_change&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Okay, synchronize again!</p>

<figure class='code'><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=''><span class='line'>[root@master1 garysawesomeenvironment]# r10k deploy environment -p</span></code></pre></td></tr></table></div></figure>


<p>And now run Puppet in the &lsquo;garysawesomeenvironment&rsquo; environment:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# puppet agent -t --environment garysawesomeenvironment
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snip fact loading&gt;
</span><span class='line'>Info: Caching catalog for master1
</span><span class='line'>Info: Applying configuration version '1392849521'
</span><span class='line'>Notice: This changes the message that already exists!!!!
</span><span class='line'>Notice: /Stage[main]/Notifyme/Notify[This changes the message that already exists!!!!]/message: defined 'message' as 'This changes the message that already exists!!!!'
</span><span class='line'>Notice: Finished catalog run in 12.54 seconds</span></code></pre></td></tr></table></div></figure>


<p>There&rsquo;s the message that I changed in the &lsquo;another_change&rsquo; branch of my &lsquo;notifyme&rsquo;
module!  What&rsquo;s it look like if I run in the &lsquo;production&rsquo; environment, though?</p>

<figure class='code'><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=''><span class='line'>
</span><span class='line'>ot@master1 garysawesomeenvironment]# puppet agent -t --environment production
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snip fact loading&gt;
</span><span class='line'>Info: Caching catalog for master1
</span><span class='line'>Info: Applying configuration version '1392848588'
</span><span class='line'>Notice: This is the changed message in the change_the_message branch
</span><span class='line'>Notice: /Stage[main]/Notifyme/Notify[This is the changed message in the change_the_message branch]/message: defined 'message' as 'This is the changed message in the change_the_message branch'
</span><span class='line'>Notice: Finished catalog run in 14.11 seconds</span></code></pre></td></tr></table></div></figure>


<p>There&rsquo;s the old message that&rsquo;s in the &lsquo;master&rsquo; branch of the &lsquo;notifyme&rsquo;
module (which is where the &lsquo;production&rsquo; branch <code>Puppetfile</code> is pointing).
To merge the changes into the production environment, we now only have to
do one thing: that&rsquo;s merge the changes in the &lsquo;another_change&rsquo; branch of
the &lsquo;notifyme&rsquo; module to the &lsquo;master&rsquo; branch &ndash; that&rsquo;s it!  Why? Because
the <code>Puppetfile</code> in the <code>production</code> branch of the <a href="https://github.com/glarizza/puppet_repository">main Puppet repo</a>
(and thus the production Puppet ENVIRONMENT) is already POINTING at the
master branch of the &lsquo;notifyme&rsquo; module.  Let&rsquo;s do the merge:</p>

<figure class='code'><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=''><span class='line'>└(~/src/puppet-notifyme)▷ git checkout master
</span><span class='line'>Switched to branch 'master'
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git merge another_change
</span><span class='line'>Updating bc3975b..608166e
</span><span class='line'>Fast-forward
</span><span class='line'> manifests/init.pp | 2 +-
</span><span class='line'> 1 file changed, 1 insertion(+), 1 deletion(-)
</span><span class='line'>
</span><span class='line'>└(~/src/puppet-notifyme)▷ git push origin master:master
</span><span class='line'>Total 0 (delta 0), reused 0 (delta 0)
</span><span class='line'>To https://github.com/glarizza/puppet-notifyme.git
</span><span class='line'>   bc3975b..608166e  master -&gt; master</span></code></pre></td></tr></table></div></figure>


<p>Another R10k synchronization is needed on the master:</p>

<figure class='code'><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=''><span class='line'>[root@master1 garysawesomeenvironment]# r10k deploy environment -p</span></code></pre></td></tr></table></div></figure>


<p>And now let&rsquo;s run Puppet in the production environment:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>[root@master1 garysawesomeenvironment]# puppet agent -t --environment production
</span><span class='line'>Info: Retrieving plugin
</span><span class='line'>&lt;snip fact loading&gt;
</span><span class='line'>Info: Caching catalog for master1
</span><span class='line'>Info: Applying configuration version '1392850004'
</span><span class='line'>Notice: This changes the message that already exists!!!!
</span><span class='line'>Notice: /Stage[main]/Notifyme/Notify[This changes the message that already exists!!!!]/message: defined 'message' as 'This changes the message that already exists!!!!'
</span><span class='line'>Notice: Finished catalog run in 11.82 seconds</span></code></pre></td></tr></table></div></figure>


<p>There&rsquo;s the message that was previously in the &lsquo;another_change&rsquo; branch that&rsquo;s
been merged to the &lsquo;master&rsquo; branch (and thus is entered into the production
Puppet environment).</p>

<h3>OR, use tags</h3>

<p>One more note &ndash; for production environments that want a BIT more stability
(rather than hoping that someone follows the policy of pushing commits to
a BRANCH of a module rather than pushing directly to master &ndash; by accident or
otherwise &ndash; and allowing that commit to make DIRECTLY it into production), the
better way is to tie all modules to some sort of release version. For modules
released to the Puppet Forge, that&rsquo;s a version, for modules stored in git
repositories, that would be a tag. Tying all modules in your production
environment (and thus production <code>Puppetfile</code>) to specific tags in git
repositories IS a &ldquo;best practice&rdquo; to ensure that the code that&rsquo;s executed in
production has some sort of &lsquo;safe guard&rsquo;.</p>

<p>TL;DR: Example tied to &lsquo;master&rsquo; branch above was demo, and not necessarily
recommended for your production needs.</p>

<h2>Holy crap, that&rsquo;s a lot to take in&hellip;</h2>

<p>Yeah, tell me about it. And, believe it or not, I&rsquo;m STILL not done with
everything that I want to talk about regarding R10k &ndash; there&rsquo;s still more
info on:</p>

<ul>
<li>Using R10k with a monolithic modules repo</li>
<li>Incorporating Hiera data</li>
<li>Triggering R10k with MCollective</li>
<li>Tying R10k to CI workflow</li>
</ul>


<p>Those will come in a later post once I have time to decide how to tackle them.
Until then, this should give you more than enough information to get started
with R10k in your own environment.</p>

<p>If you have any questions/comments/corrections, PLEASE enter them in the
comments below and I&rsquo;ll be happy to respond when I&rsquo;m not flying from gig to
gig! :)  Cheers!</p>

<p><strong>EDIT</strong>: 2/19/2014 &ndash; correct librarian-puppet assumption thanks to Reid Vandewiele</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Functional Puppet Workflow Part 2: Roles and Profiles]]></title>
    <link href="http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/"/>
    <updated>2014-02-17T16:01:37-06:00</updated>
    <id>http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2</id>
    <content type="html"><![CDATA[<p>In my <a href="http://www.garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/">first post</a>, I talked about writing functional component
modules. Well, I didn&rsquo;t really do much detailing other than pointing out
key bits of information that tend to cause problems. In this post, I&rsquo;ll
describe the next layer to the functional Puppet module workflow.</p>

<p>People usually stop once they have a library of component modules (whether
hand-written, taken from Github, or pulled from <a href="http://forge.puppetlabs.com">The Forge</a>). The idea
is that you can classify all of your nodes in <code>site.pp</code>, the Puppet
Enterprise Console, The Foreman, or with some other ENC, so why not just
declare all your classes for every node when you need them?</p>

<p>Because that&rsquo;s a lot of extra work and opportunities for fuckups.</p>

<p>People recognized this, so in the EARLY days of Puppet they would create node
blocks in <code>site.pp</code> and use inheritance to inherit from those blocks. This was
the right IDEA, but probably not the best PLACE for it. Eventually, &lsquo;Profiles&rsquo;
were born.</p>

<p>The idea of &lsquo;Roles and Profiles&rsquo; originally came from a piece that
<a href="http://www.craigdunn.org/2012/05/239/">Craig Dunn wrote</a> while he worked for the BBC, and then
<a href="http://sysadvent.blogspot.com/2012/12/day-13-configuration-management-as-legos.html">Adrien Thebo also wrote a piece</a> that documents the same sort of pattern.
So why am I writing about it a THIRD time? Well, because I feel it&rsquo;s only a PIECE
of an overall puzzle. The introduction of Hiera and other awesome tools (like R10k,
which we will get to on the next post) still make Roles and Profiles VIABLE, but
they also extend upon them.</p>

<p>One final note before we move on &ndash; the terms &lsquo;Roles&rsquo; and &lsquo;Profiles&rsquo; are ENTIRELY
ARBITRARY. They&rsquo;re not magic reserve words in Puppet, and you can call them
whatever the hell you want. It&rsquo;s also been pointed out that Craig MIGHT have
misnamed them (a ROLE should be a model for an individual piece of tech, and a
PROFILE should probably be a group of roles), but, like all good Puppet Labs
employees &ndash; we suck at naming things.</p>

<h2>Profiles: technology-specific wrapper classes</h2>

<p>A profile is simply a wrapper class that groups Hiera lookups and class
declarations into one functional unit. For example, if you wanted Wordpress
installed on a machine, you&rsquo;d probably need to declare the apache class to
get Apache setup, declare an <code>apache::vhost</code> for the Wordpress directory,
setup a MySQL database with the appropriate classes, and so on. There are
a lot of components that go together when you setup a piece of technology,
it&rsquo;s not just a single class.</p>

<p>Because of this, a profile exists to give you a single class you can include
that will setup all the necessary bits for that piece of technology (be it
Wordpress, or Tomcat, or whatever).</p>

<p>Let&rsquo;s look at a simple profile for Wordpress:</p>

<figure class='code'><figcaption><span>profiles/manifests/wordpress.pp </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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">profiles::wordpress</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Hiera lookups</span>
</span><span class='line'>  <span class="nv">$site_name</span>               <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::site_name&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_user_password</span> <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_user_password&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$mysql_root_password</span>     <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::mysql_root_password&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_db_host</span>       <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_db_host&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_db_name</span>       <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_db_name&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_db_password</span>   <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_db_password&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_user</span>          <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_user&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_group</span>         <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_group&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_docroot</span>       <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_docroot&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_port</span>          <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_port&#39;</span><span class="p">)</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Create user</span>
</span><span class='line'>  <span class="nc">group</span> <span class="p">{</span> <span class="s1">&#39;wordpress&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">name</span>   <span class="p">=&gt;</span> <span class="nv">$wordpress_group</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="nc">user</span> <span class="p">{</span> <span class="s1">&#39;wordpress&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>   <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">gid</span>      <span class="p">=&gt;</span> <span class="nv">$wordpress_group</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">password</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_user_password</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">name</span>     <span class="p">=&gt;</span> <span class="nv">$wordpress_group</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">home</span>     <span class="p">=&gt;</span> <span class="nv">$wordpress_docroot</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Configure mysql</span>
</span><span class='line'>  <span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;mysql::server&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">root_password</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_root_password</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;mysql::bindings&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">php_enable</span> <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Configure apache</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">apache</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">apache::mod::php</span>
</span><span class='line'>  <span class="nc">apache::vhost</span> <span class="p">{</span> <span class="nv">$::fqdn</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">port</span>    <span class="p">=&gt;</span> <span class="nv">$wordpress_port</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">docroot</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_docroot</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Configure wordpress</span>
</span><span class='line'>  <span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;::wordpress&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">install_dir</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_docroot</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">db_name</span>     <span class="p">=&gt;</span> <span class="nv">$wordpress_db_name</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">db_host</span>     <span class="p">=&gt;</span> <span class="nv">$wordpress_db_host</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">db_password</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_db_password</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<h3>Name your profiles according to the technology they setup</h3>

<p>Profiles are technology-specific, so you&rsquo;ll have one to setup wordpress, and
tomcat, and jenkins, and&hellip;well, you get the picture. You can also namespace
your profiles so that you have <code>profiles::ssh::server</code> and
<code>profiles::ssh::client</code> if you want. You can even have
<code>profiles::jenkins::tomcat</code> and <code>profiles::jenkins::jboss</code> or however you need
to namespace according to the TECHNOLOGIES you use. You don&rsquo;t need to include
your environment in the profile name (a la <code>profiles::dev::tomcat</code>) as the bits
of data that make the dev environment different from production should come
from <strong>HIERA</strong>, and thus aren&rsquo;t going to be different on a per-profile basis.
You CAN setup profiles according to your business unit if multiple units use
Puppet and have different setups (a la <code>security::profiles::tomcat</code> versus
<code>ops::profiles::tomcat</code>), but the GOAL of Puppet is to have one main set of
modules that every group uses (and the Hiera data being different for every
group). That&rsquo;s the GOAL, but I&rsquo;m pragmatic enough to understand that not
everywhere is a shiny, happy &lsquo;DevOps Garden.&rsquo;</p>

<h3>Do all Hiera lookups in the profile</h3>

<p>You&rsquo;ll see that I declared variables and set their values with Hiera lookups.
The profile is the place for these lookups because the profile collects all
external data and declares all the classes you&rsquo;ll need. In reality, you&rsquo;ll
<strong>USUALLY</strong> only see profiles looking up parameters and declaring classes
(i.e. declaring users and groups like I did above will USUALLY be left to
component classes).</p>

<p>I do the Hiera lookups first to make it easy to debug from where those values
came. I don&rsquo;t rely on <a href="http://docs.puppetlabs.com/hiera/1/puppet.html#automatic-parameter-lookup">&lsquo;Automatic Parameter Lookup&rsquo;</a> in Puppet
3.x.x because it can be &lsquo;magic&rsquo; for people who aren&rsquo;t aware of it (for people
new to Puppet, it&rsquo;s much easier to see a function call and trace back what it
does rather than experience Puppet doing something unseen and wondering what
the hell happened).</p>

<p>Finally, you&rsquo;ll notice that my Hiera lookups have <strong>NO DEFAULT VALUES</strong> &ndash; this
is <strong>BY DESIGN!</strong>  For most people, their Hiera data is <strong>PROBABLY</strong> located in
a separate repository as their Puppet module data. Imagine making a change to
your profile to have it lookup a bit of data from Hiera, and then imagine you
FORGOT to put that data into Hiera. What happens if you provide a default value
to Hiera? The catalog compiles, that default value gets passed down to the
component module, and gets enforced on disk. If you have good tests, you MIGHT
see that the component you configured has a bit of data that&rsquo;s not correct, but
what if you don&rsquo;t have a great post-Puppet testing workflow? Puppet will
correctly set this default value, according to Puppet everything is green and
worked just fine, but now your component is setup incorrectly. That&rsquo;s one of
the WORST failures &ndash; the ones that you don&rsquo;t catch.  Now, imagine you DON&rsquo;T
provide a default value. In THIS case, Puppet will raise a compilation error
because a Hiera lookup didn&rsquo;t return a value. You&rsquo;ll catch your error before
anything gets pushed to Production and you can catch the screwup. This is
a MUCH better solution.</p>

<h3>Use parameterized class declarations and explicitly pass values you care about</h3>

<p>The parameterized class declaration syntax can be dangerous. The difference
between the <code>include</code> function and the parameterized class syntax is that
the <code>include</code> function is idempotent. You can do the following in a Puppet
manifest, and Puppet doesn&rsquo;t raise an error:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>include apache
</span><span class='line'>include apache
</span><span class='line'>include apache</span></code></pre></td></tr></table></div></figure>


<p>This is because the <code>include</code> function checks to see if the class is in the
catalog. If it ISN&rsquo;T, then it adds it. If it IS, then it exits cleanly. The
<code>include</code> function is your pal.</p>

<p>Consider THIS manifest:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>class { 'apache': }
</span><span class='line'>include apache
</span><span class='line'>include apache</span></code></pre></td></tr></table></div></figure>


<p>Does this work?  Yep.  The parameterized class syntax adds the class to the
catalog, the include function detects this and exits cleanly twice.  What
about THIS manifest:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>include apache
</span><span class='line'>class { 'apache': }
</span><span class='line'>include apache</span></code></pre></td></tr></table></div></figure>


<p>Does THIS work?  Nope!  Puppet raises a compilation error because a class was
declared more than once in a catalog.  Why?  Well, consider that Puppet is
&lsquo;declarative&rsquo;&hellip;all the way up until it isn&rsquo;t.  Puppet&rsquo;s PARSER reads from the
top of the file to the bottom of the file, and we have a single-pass parser
when it comes to things like setting variables and declaring classes. When
the parser hits the first include function, it adds the class to the catalog.
The parameterized class syntax, however, is a honey badger: it doesn&rsquo;t give
a shit. It adds a class to the catalog regardless of whether it already exists
or not. So why would we EVER use the parameterized class declaration syntax?
We need to use it because the include function doesn&rsquo;t allow you to pass
parameters when you declare a class.</p>

<p>So wait &ndash; why did I spend all this time explaining why the parameterized class
syntax is more dangerous than the include function <strong>ONLY</strong> to recommend its
use in profiles?  For two reasons:</p>

<ul>
<li>We need to use it to pass parameters to classes</li>
<li>We&rsquo;re wrapping its use in a class that we can <strong>IN TURN</strong> declare with the <code>include</code> function</li>
</ul>


<p>Yes, we can get the best of BOTH worlds, the ability to pass parameters and
the use of our pal the <code>include</code> function, with this wrapper class. We&rsquo;ll see
the latter usage when we come to roles, but for now let&rsquo;s focus on passing
parameter values.</p>

<p>In the first section, we set variables with Hiera lookups, now we can pass
those variables to classes we&rsquo;re declaring with the parameterized class syntax.
This allows the declaration of the class to be static, but the parameters we
pass to that class to change according to the Hiera hierarchy. We&rsquo;ve explicitly
called the <code>hiera</code> function, so it makes it easier to debug, and we&rsquo;re explicitly
passing parameter values so we know definitively which parameters are being
passed (and thus are overriding default values) to the component module. Finally,
since our component modules do NOT use Hiera at all, we can be sure that if we&rsquo;re
not passing a parameter that it&rsquo;s getting its value from the default set in the
module&rsquo;s <code>::params</code> class.</p>

<p>Everything we do here is meant to make things easier to debug when it&rsquo;s 3am and
things aren&rsquo;t working. Any asshole can do crazy shit in Puppet, but a seasoned
sysadmin writes their code for ease of debugging during 3am pages.</p>

<h3>An annoying Puppet bug &ndash; top-level class declarations and profiles</h3>

<p><a href="http://projects.puppetlabs.com/issues/2053">Oh, ticket 2053</a>, how terrible are you? This is one of those bug numbers
that I can remember by heart (like <a href="http://projects.puppetlabs.com/issues/8040">8040</a> and <a href="http://projects.puppetlabs.com/issues/86">86</a>). Puppet has
the ability to do &lsquo;relative namespacing&rsquo;, which allows you to declare a variable
called <code>$port</code> in a class called <code>$apache</code> and refer to it as <code>$port</code> instead
of fully-namespacing the variable, and thus having to call it <code>$apache::port</code>
inside the <code>apache</code> class. It&rsquo;s a shortcut &ndash; you can STILL refer to the variable
as <code>$apache::port</code> in the class &ndash; but it comes in handy. The PROBLEM occurs when
you create a profile, as we did above, called <code>profiles::wordpress</code> and you try
to declare a class called <code>wordpress</code>.  If you do the following inside the
<code>profiles::wordpress</code> class, what class is being declared:</p>

<figure class='code'><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=''><span class='line'>include wordpress</span></code></pre></td></tr></table></div></figure>


<p>If you think you&rsquo;re declaring a wordpress class from within a wordpress module
in your Puppet modulepath, you would be wrong.  Puppet ACTUALLY thinks you&rsquo;re
trying to declare <code>profiles::wordpress</code> because you&rsquo;re INSIDE the <code>profiles::wordpress</code>
class and it&rsquo;s doing relative namespacing (i.e. in the same way you refer to <code>$port</code>
and ACTUALLY mean <code>$apache::port</code> it thinks you&rsquo;re referring to <code>wordpress</code> and
ACTUALLY mean <code>profiles::wordpress</code>.</p>

<p>Needless to say, this causes LOTS of confusion.</p>

<p>The solution here is to declare a class called <code>::wordpress</code> which tells Puppet
to go to the top-level namespace and look for a module called <code>wordpress</code> which
has a top-level class called <code>wordpress</code>. It&rsquo;s the same reason that we refer to
Facter Fact values as <code>$::osfamily</code> instead of <code>$osfamily</code> in class definitions
(because you can declare a local variable called <code>$osfamily</code> in your class).
This is why in the profile above you see this:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>class { '::wordpress':
</span><span class='line'>  install_dir =&gt; $wordpress_docroot,
</span><span class='line'>  db_name     =&gt; $wordpress_db_name,
</span><span class='line'>  db_host     =&gt; $wordpress_db_host,
</span><span class='line'>  db_password =&gt; $wordpress_db_password,
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>When you use profiles and roles, you&rsquo;ll need to do this namespacing trick when
declaring classes because you&rsquo;re frequently going to have a <code>profile::&lt;sometech&gt;</code>
that will declare the <code>&lt;sometech&gt;</code> top-level class.</p>

<h2>Roles: business-specific wrapper classes</h2>

<p>How do you refer to your machines? When I ask you about that cluster over
there, do you say &ldquo;Oh, you mean the machines with java 1.6, apache, mysql,
etc&hellip;&rdquo;?  I didn&rsquo;t think so. You usually have names for them, like the
&ldquo;internal compute cluster&rdquo; or &ldquo;app builder nodes&rdquo; or &ldquo;DMZ repo machines&rdquo; or
whatever. These names are your Roles. Roles are just the mapping of your
machine&rsquo;s names to the technology that should be ON them. In the past we had
descriptive hostnames that afforded us a code for what the machine &lsquo;did&rsquo; &ndash;
roles are just that mapping for Puppet.</p>

<p>Roles are namespaced just like profiles, but now it&rsquo;s up to your organization
to fill in the blanks. Some people immediately want to put environments into
the roles (a la <code>roles::uat::compute_cluster</code>), but that&rsquo;s usually not necessary
(as MOST LIKELY the compute cluster nodes have the SAME technology on them
when they&rsquo;re in dev versus when they&rsquo;re in prod, it&rsquo;s just the DATA &ndash; like
database names, VIP locations, usernames/passwords, etc &ndash; that&rsquo;s different.
Again, these data differences will come from Hiera, so there should be no reason
to put the environment name in your role). You still CAN put the environment
name in the role if it makes you feel better, but it&rsquo;ll probably be useless.</p>

<h3>Roles ONLY include profiles</h3>

<p>So what exactly is in the role wrapper class? That depends on what technology
is on the node that defines that role. What I can tell you for CERTAIN is that
roles should ONLY use the <code>include</code> function and should ONLY include profiles.
What does this give us? This gives us our pal the <code>include</code> function back! You
can include the same profile 100 times if you want, and Puppet only puts it in
the catalog once.</p>

<h3>Every node is classified with one role. Period.</h3>

<p>The beautiful thing about roles and profiles is that the GOAL is that you
should be able to classify a node with a SINGLE role and THAT&rsquo;S IT. This makes
classification simple and static &ndash; the node gets its role, the role includes
profiles, profiles call out to Hiera for data, that data is passed to component
modules, and away we go. Also, since classification is static, you can use
version control to see what changes were introduced to the role (i.e. what
profiles were added or removed).  In my opinion, if you need to apply more
than one role to a node, you&rsquo;ve introduced a new role (see below).</p>

<h3>Roles CAN use inheritance&hellip;if you like</h3>

<p>I&rsquo;ve seen people implement roles a couple of different ways, and one of them
is to use inheritance to build a catalog.  For example, you can define a base
<code>roles</code> class that includes something like a base security profile (i.e.
something that EVERY node in your infrastructure should have). Moving down the
line, you COULD namespace according to function like <code>roles::app</code> for your
application server machines. The <code>roles::app</code> class could inherit from the <code>roles</code>
class (which gets the base security profile), and could then include the profiles
necessary to setup an application server. Next, you could subclass down to
<code>roles::app::site_foo</code> for an application server that supports some site in
your organization.  That class inherits from the <code>roles::app</code> class, and then
adds profiles that are specific to that site (maybe they use Jboss instead of
Tomcat, and thus that&rsquo;s where the differentiation occurs). This is great
because you don&rsquo;t have a lot of repeated use of the <code>include</code> function, but
it also makes it hard to definitively look at a specific role to see exactly
what&rsquo;s being declared (i.e. all the profiles). You have to weigh what you
value more: less typing or greater visibility. I will err on the side of
greater visibility (just due to that whole 3am outage thing), but it&rsquo;s up
to you to decide what to optimize for.</p>

<h3>A role similar, yet different, from another role is: a new role</h3>

<p>EVERYBODY says to me &ldquo;Gary, I have this machine that&rsquo;s an AWFUL LOT like
this role over here, but&hellip;it&rsquo;s different.&rdquo; My answer to them is: &ldquo;Great,
that&rsquo;s another role.&rdquo;  If the thing that&rsquo;s different is data (i.e. which
database to connect to, or what IP address to route traffic through),
then that difference should be put in HIERA and the classification should
remain the same. If that difference is technology-specific (i.e. this server
uses JBoss instead of Tomcat) then first look and see if you can isolate
how you know this machine is different (maybe it&rsquo;s on a different subnet,
maybe it&rsquo;s at a different location, something like that). If you can figure
that out and write a Fact for it (or use similar conditional logic to determine
this logically), then you can just drop that conditional logic in your role
and let it do the heavy lifting. If, in the end, this bit of data is totally
arbitrary, then you&rsquo;ll need to create another role (perhaps a subclass using
the above namespacing) and assign it to your node.</p>

<p>The hardest thing about this setup is naming your roles. Why? Every site is
different.  It&rsquo;s hard for me to account for differences in your setup because
your workplace is dysfunctional (seriously).</p>

<h2>Review: what does this get you?</h2>

<p>Let&rsquo;s walk through every level of this setup from the top to the bottom and see
what it gets you. Every node is classified to a single role, and, for the most
part, that classification isn&rsquo;t going to change. Now you can take all the extra
work off your classifier tool and put it back into the manifests (that are
subject to version control, so you can <code>git blame</code> to your heart&rsquo;s content and
see who last changed the role/profile). Each role is going to include one or
more profile, which gives us the added idempotent protection of the include
function (of course, if profiles have collisions with classes you&rsquo;ll have to
resolve those. Say one or more profiles tries to include an apache class &ndash;
simply break that component out into a separate profile, extract the parameters
from Hiera, and include that profile at a higher level). Each profile is going
to do Hiera lookups which should give you the ability to provide different data
for different host types (i.e. different data on a per-environment level, or
however you lay out your Hiera hierarchy), and that data will be passed
directly to class that is declared. Finally, each component module will
accept parameters as variables internal to that module, default
parameters/variables to sane values in the <code>::params</code> class, and use those
variables when declaring each resource throughtout its classes.</p>

<ul>
<li>Roles abstract profiles</li>
<li>Profiles abstract component modules</li>
<li>Hiera abstracts configuration data</li>
<li>Component modules abstract resources</li>
<li>Resources abstract the underlying OS implementation</li>
</ul>


<h2>Choose your level of comfortability</h2>

<p>The roles and profiles pattern also buys you something else &ndash; the ability for
less-skilled and more-skilled Puppet users to work with the same codebase.
Let&rsquo;s say you use some GUI classifier (like the Puppet Enterprise Console),
someone who&rsquo;s less skilled at Puppet looks and sees that a node is classified
with a certain role, so they open the role file and see something like this:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>include profiles::wordpress
</span><span class='line'>include profiles::tomcat
</span><span class='line'>include profiles::git::repo_server</span></code></pre></td></tr></table></div></figure>


<p>That&rsquo;s pretty legible, right? Someone who doesn&rsquo;t regularly use Puppet can
probably make a good guess as to what&rsquo;s on the machine. Need more information?
Open one of the profiles and look specifically at the classes that are being
declared. Need to know the data being passed? Jump into Hiera. Need to know
more information? Dig into each component module and see what&rsquo;s going on there.</p>

<p>When you have everything abstracted correctly, you can have developers
providing data (like build versions) to Hiera, junior admins grouping nodes for
classification, more senior folk updating profiles, and your best Puppet people
creating/updating component modules and building plugins like custom
facts/functions/whatever.</p>

<h2>Great! Now go and refactor&hellip;</h2>

<p>If you&rsquo;ve used Puppet for more than a month, you&rsquo;re probably familiar with the
&ldquo;Oh shit, I should have done it THAT way&hellip;let me refactor this&rdquo; game. I know,
it sucks, and we at Puppet Labs haven&rsquo;t been shy of incorporating something that
we feel will help people out (but will also require some refactoring). This
pattern, though, has been in use by the Professional Services team at Puppet Labs
for over a year without modification. I&rsquo;ve used this on sites GREAT and small,
and every site with which I&rsquo;ve consulted and implemented this pattern has been
able to both understand its power and derive real value within a week. If you&rsquo;re
contemplating a refactor, you can&rsquo;t go wrong with Roles and Profiles (or
whatever names you decide to use).</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Functional Puppet Workflow Part 1: Module Structure]]></title>
    <link href="http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/"/>
    <updated>2014-02-17T16:01:37-06:00</updated>
    <id>http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1</id>
    <content type="html"><![CDATA[<p>Working as a professional services engineer for <a href="http://www.puppetlabs.com">Puppet Labs</a>,
my life consists almost entirely of either correcting some of the worst code
atrocities you&rsquo;ve seen in your life, or helping people get started with Puppet
so that they don&rsquo;t need to call us again due to: A.) Said code atrocities or
B.) Refactor the work we JUST helped them start.  It wasn&rsquo;t ALWAYS like this &ndash;
I can remember some of my earliest gigs, and I almost feel like I should go
revisit them if only to correct some of the previous &lsquo;best practices&rsquo; that
didn&rsquo;t quite pan out.</p>

<p>This would be exactly why I&rsquo;m wary of &lsquo;Best Practices&rsquo; &ndash; because one person&rsquo;s
&lsquo;Best Practice&rsquo; is another person&rsquo;s &lsquo;What the fuck did you just do?!&rsquo;</p>

<p>Having said that, I&rsquo;m finding myself repeating a story over and over again when
I train/consult, and that&rsquo;s the story of &lsquo;The Usable Puppet Workflow.&rsquo;  Everybody
wants to know &lsquo;The Right Way™&rsquo;, and I feel like we finally have a way that survives
a reasonable test of time. I&rsquo;ve been promoting this workflow for over a year (which
is a HELL of a long time in Startup time), and I&rsquo;ve yet to really see an edge case
it couldn&rsquo;t handle.</p>

<p>(If you&rsquo;re already savvy: yes, this is the Roles and Profiles talk)</p>

<p>I&rsquo;ll be breaking this workflow down into separate blog posts for every component,
and, as always, your comments are welcome&hellip;</p>

<h2>It all starts with the component module</h2>

<p>The first piece of a functional Puppet deployment starts with what we call
&lsquo;component modules&rsquo;.  Component modules are the lowest level in your
deployment, and are modules that configure specific pieces of technology (like
apache, ntp, mysql, and etc&hellip;).  Component modules are well-encapsulated, have
a reasonable API, and focus on doing small, specific things really well (i.e.
the *nix way).</p>

<p>I don&rsquo;t want to write thousands of words on building component modules because
I feel like others have done this better than I. As examples, check out
<a href="http://www.devco.net/archives/2012/12/13/simple-puppet-module-structure-redux.php">RI&rsquo;s Post on a simple module structure</a>,
<a href="http://docs.puppetlabs.com/guides/module_guides/bgtm.html">Puppet Labs&#8217; very own docs on the subject</a>,
<a href="http://www.confreaks.com/videos/1651-puppetconf2012-puppet-modules-for-fun-and-profit">and even Alessandro&rsquo;s Puppetconf 2012 session.</a> Instead, I&rsquo;d like to
provide some pointers on what I feel makes a good component module, and some
&lsquo;gotchas&rsquo; we&rsquo;ve noticed.</p>

<h3>Parameters are your API</h3>

<p>In the current world of Puppet, you MUST define the parameters your module will
accept in the Puppet DSL. Also, every parameter MUST ultimately have a value
when Puppet compiles the catalog (whether by explicitly passing this parameter
value when declaring the class, or by assuming a default value). Yes, it&rsquo;s
funny that, when writing a Puppet class, if you typo a VARIABLE Puppet will
not alert you to this (in a NON <code>use strict</code>-ian sort of approach) and will
happily accept a variable in an undefined state, but the second you don&rsquo;t pass
a value to your class parameter you&rsquo;re in for a rude compilation error.
This is the way of Puppet classes at the time of this writing, so you&rsquo;re going
to see Puppet classes with LINES of defined parameters. I expect this to change
in the future (please let this change in the near future), but for now, it&rsquo;s
a necessary evil.</p>

<p>The parameters you expose to your top-level class (i.e. given class names like
<code>apache</code> and <code>apache::install</code>, I&rsquo;m talking specifically about <code>apache</code>) should
be treated as an API to your module. IDEALLY, they&rsquo;re the ONLY THING that
a user needs to modify when using your module. Also, whenever possible, it
should be the case that a user need ONLY interact with the top-level class when
using your module (of course, defined resource types like <code>apache::vhost</code> are
used on an ad-hoc basis, and thus are the exception here).</p>

<h3>Inherit the <code>::params</code> class</h3>

<p>We&rsquo;re starting to make enemies at this point. It&rsquo;s been a convention for modules
to use a <code>::params</code> class to assign values to all variables that are going to
be used for all classes inside the module. The idea is that the <code>::params</code> class
is the one-stop-shop to see where a variable is set. Also, to get access to a
variable that&rsquo;s set in a Puppet class, you have to declare the class (i.e. use
the <code>include()</code> function or inherit from that class). When you declare a class
that has both variables AND resources, those resources get put into the catalog,
which means that Puppet ENFORCES THE STATE of those resources. What if you only
needed a variable&rsquo;s value and didn&rsquo;t want to enforce the rest of the resources
in that class? There&rsquo;s no good way in Puppet to do that. Finally, when you inherit
from a class in Puppet that has assigned variable values, you ALSO get access
to those variables in the parameter definition section of your class (i.e. the
following section of the class:</p>

<pre><code>class apache (
  $port = $apache::params::port,
  $user = $apache::params::user,
) inherits apache::params {
</code></pre>

<p>See how I set the default value of <code>$apache::port</code> to <code>$apache::params::port</code>?
I could only access the value of the variable <code>$apache::params::port</code> in that
section by inheriting from the <code>apache::params</code> class.  I couldn&rsquo;t insert
<code>include apache::params</code> below that section and be allowed access to the variable
up in the parameter defaults section (due to the way that Puppet parses classes).</p>

<p><strong>FOR THIS REASON, THIS IS THE </strong><em>ONLY</em><strong> RECOMMENDED USAGE OF INHERITANCE IN
PUPPET!</strong></p>

<p>We do NOT recommend using inheritance anywhere else in Puppet and for any other
reason because there are better ways to achieve what you want to do INSTEAD of
using inheritance.  Inheritance is a holdover from a scarier, more lawless time.</p>

<p><strong>NOTE: Data in Modules</strong> &ndash; There&rsquo;s a &lsquo;Data in Modules&rsquo; pattern out there that
attempts to eliminate the <code>::params</code> class.  <a href="http://garylarizza.com/blog/2013/12/08/when-to-hiera/">I wrote about it in a previous post</a>,
and I recommend you read that post for more info (it&rsquo;s near the bottom).</p>

<h3>Do <strong>NOT</strong> do Hiera lookups in your component modules!</h3>

<p>This is something that&rsquo;s really only RECENTLY been pushed. When Hiera was
released, we quickly recognized that it would be the answer to quite a few
problems in Puppet. In the rush to adopt Hiera, many people started adding
Hiera calls to their modules, and suddenly you had &lsquo;Hiera-compatible&rsquo; modules
out there. This caused all kinds of compatibility problems, and it was largely
because there wasn&rsquo;t a better module structure and workflow by which to integrate
Hiera. The pattern that I&rsquo;ll be pushing DOES INDEED use Hiera, <strong>BUT</strong> it
confines all Hiera calls to a higher-level wrapper class we call a &lsquo;profile&rsquo;.
The reasons for NOT using Hiera in your module are:</p>

<ul>
<li>By doing Hiera calls at a higher level, you have a greater visibility on
exactly what parameters were set by Hiera and which were set explicitly or by
default values.</li>
<li>By doing Hiera calls elsewhere, your module is backwards-compatible for
those folks who are NOT using Hiera</li>
</ul>


<p>Remember &ndash; your module should just accept a value and use it somewhere. Don&rsquo;t
get <strong>TOO</strong> smart with your component module &ndash; leave the logic for other
places.</p>

<h3>Keep your component modules generic</h3>

<p>We always get asked &ldquo;How do I know if I&rsquo;m writing a good module?&rdquo; We USED to
say &ldquo;Well, does it work?&rdquo; (and trust me, that was a BIG hurdle). Now, with
data separation models out there like Hiera, I have a couple of other questions
that I ask (you know, BEYOND asking if it compiles and actually installs the
thing it&rsquo;s supposed to install). The best way I&rsquo;ve found to determine if your
module is &lsquo;generic enough&rsquo; is if I asked you TODAY to give me your module,
would you give it to me, or would you be worried that there was some
company-specific data locked in there? If you have company-specific data in
your module, then you need to refactor the module, store the data in Hiera, and
make your module more generic/reusable. Also, does your module focus on installing
one piece of technology, or are you declaring packages for shared libraries
or other components (like gcc, apache, or other common components)? You&rsquo;re
not going to win any prizes for having the biggest, most monolithic module
out there. Rather, if your module is that large and that complex, you&rsquo;re
going to have a hell of a time debugging it. Err on the side of making your
modules smaller and more task-specific. So what if you end up needing to
declare 4 classes where you previously declared 1? In the roles and profiles
pattern we will show you in the next blog post, you can abstract that away
ANYHOW.</p>

<h3>Don&rsquo;t play the &ldquo;what if&rdquo; game</h3>

<p>I&rsquo;ve had more than a couple of gigs where the customer says something along the
lines of &ldquo;What if we need to introduce FreeBSD/Solaris/etc&hellip; nodes into our
organization, shouldn&rsquo;t I account for them now?&rdquo; This leads more than a few
people down a path of entirely too-complex modules that become bulky and
unwieldy. Yes, your modules should be formatted so that you can simply add
another case in your <code>::params</code> class for another OS&rsquo;s parameters, and yes,
your module should be formatted so that your <code>::install</code> or <code>::config</code>
class can handle another OS, but if you currently only manage Redhat, and
you&rsquo;ve only EVER managed Redhat, then don&rsquo;t start adding Debian parameters
RIGHT NOW just because you&rsquo;re afraid you might inherit Ubuntu machines. The
goal of Puppet is to automate the tasks that eat up the MAJORITY of your time
so you can focus on the edge cases that really demand your time. If you can
eventually automate those edge cases, then AWESOME! Until then, don&rsquo;t spend
the majority of your time trying to automate the edge cases only to drown
under the weight of deadlines from simple work that you COULD have already
automated (but didn&rsquo;t, because you were so worried about the exceptions)!</p>

<h3>Store your modules in version control</h3>

<p>This should go without saying, but your modules should be stored in version
control (a la git, svn, hg, whatever). We tend to prefer git due to its lightweight
branching and merging (most of our tooling and solutions will use git because
we&rsquo;re big git users), but you&rsquo;re free to use whatever you want. The bigger
question is HOW to store your modules in version control. There are usually
two schools of thought:</p>

<ul>
<li>One repository per module</li>
<li>All modules in a single repository</li>
</ul>


<p>Each model has its pros and cons, but we tend to recommend one module per
repository for the following reasons:</p>

<ul>
<li>Individual repos mean individual module development histories</li>
<li>Most VCS solutions don&rsquo;t have per-folder ACLs for a single repositories;
having multiple repos allows per-module security settings.</li>
<li>With the one-repository-per-module solution, modules you pull down from the
Forge (or Github) must be committed to your repo. Having multiple
repositories for each module allow you to keep everything separate</li>
</ul>


<p><strong>NOTE:</strong> This becomes important in the third blog post in the series when we
talk about moving changes to each Puppet Environment, but it&rsquo;s important to
introduce it NOW as a &lsquo;best practice&rsquo;. If you use our recommended module/environment
solution, then one-module-per-repo is the best practice. If you DON&rsquo;T use our
solution, then the single repository per for all modules will STILL work,
but you&rsquo;ll have to manage the above issues. Also note that even if you currently
have every module in a single repository, you can STILL use our solution in
part 3 of the series (you&rsquo;ll just need to perform a couple of steps to conform).</p>

<h2>Best practices are shit</h2>

<p>In general, &lsquo;best practices&rsquo; are only recommended if they fit into your organizational
workflow. The best and worst part of Puppet is that it&rsquo;s infinitely customizable,
so &lsquo;best practices&rsquo; will invariably be left wanting for a certain subset of the
community. As always, take what I say under consideration; it&rsquo;s quite possible
that I could be entirely full of shit.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Seriously, What Is This Provider Doing?]]></title>
    <link href="http://garylarizza.com/blog/2013/12/15/seriously-what-is-this-provider-doing/"/>
    <updated>2013-12-15T10:44:17-06:00</updated>
    <id>http://garylarizza.com/blog/2013/12/15/seriously-what-is-this-provider-doing</id>
    <content type="html"><![CDATA[<p>Clarke&rsquo;s third law states: &ldquo;Any sufficiently advanced technology is
indistinguishable from magic.&rdquo; In the case of Ruby and Puppet provider
interaction, I&rsquo;m inclined to believe it. If you want proof, take a look at some
of the native Puppet types &ndash; no amount of &lsquo;Expecto Patronum&rsquo; will free you from
the Ruby metaprogramming dementors that hover around
<code>lib/puppet/provider/exec</code>-land.</p>

<p>In my <a href="http://garylarizza.com/blog/2013/11/25/fun-with-providers/">first post tackling Puppet types and providers</a>, I introduced
the concept of Puppet types and the utility they provide. <a href="http://garylarizza.com/blog/2013/11/26/fun-with-providers-part-2/">In the second post,</a>
I brought you to the great plain of Puppet providers and introduced the core
methods necessary for creating a very basic Puppet provider with a single property
(HINT: if you&rsquo;ve not read either of those posts, or you&rsquo;ve never dealt with basic
types and providers, you might want to stop here and read up a bit on the topics).
The problems with a provider like the one created in that post were:</p>

<ul>
<li><code>puppet resource</code> support wasn&rsquo;t implemented, so you couldn&rsquo;t query for existing instances of the type on the system (and their corresponding values)</li>
<li>The getter method would be called for EVERY instance of the type on the system, which would mean shelling-out multiple times during a run</li>
<li>Ditto for the setter method (if changes to multiple instances of the type were necessary)</li>
<li>That type was VERY basic (i.e. ensurable with a single property)</li>
</ul>


<p>Unfortunately, when most of us have the need of a Puppet type and provider, we
usually require multiple properties and reasonably complex system interaction. When
it comes to creating both a getter and a setter method for every property (including
the potential performance hit that could come from shelling-out many times during
a Puppet run), ain&rsquo;t nobody got time for that. And finally, <code>puppet resource</code> is a
REALLY handy tool for querying the current state of your resources on a system.
These problems all have solutions, but up until recently there was just one more
problem:</p>

<p><strong>Good luck finding documentation for those solutions.</strong></p>

<p>NOTE: The <a href="http://www.amazon.com/Puppet-Types-Providers-Dan-Bode/dp/1449339328/ref=sr_1_1?ie=UTF8&amp;qid=1387136838&amp;sr=8-1&amp;keywords=types+and+providers+puppet+book">Puppet Types and Providers book written by Nan and Dan</a>
is a great resource that provides a bit of a deeper dive than I&rsquo;ll be doing in this
post &ndash; DO check it out if you want to know more</p>

<h2>Something, something, <code>puppet resource</code></h2>

<p>The <code>puppet resource</code> command (or <code>ralsh</code>, as it used to be known), is a very
handy command for querying a system and returning the current state of resources
for a specific Puppet type.  Try it out if you never have (note that the following
is being run on CentOS 6.4):</p>

<pre><code>[root@linux ~]# puppet resource user
user { 'abrt':
  ensure           =&gt; 'present',
  gid              =&gt; '173',
  home             =&gt; '/etc/abrt',
  password         =&gt; '!!',
  password_max_age =&gt; '-1',
  password_min_age =&gt; '-1',
  shell            =&gt; '/sbin/nologin',
  uid              =&gt; '173',
}
user { 'adm':
  ensure           =&gt; 'present',
  comment          =&gt; 'adm',
  gid              =&gt; '4',
  groups           =&gt; ['sys', 'adm'],
  home             =&gt; '/var/adm',
  password         =&gt; '*',
  password_max_age =&gt; '99999',
  password_min_age =&gt; '0',
  shell            =&gt; '/sbin/nologin',
  uid              =&gt; '3',
}
&lt; ... and more users below ... &gt;
</code></pre>

<p>The <code>puppet resource</code> command returns a list of all users on the system
and their current property values (note you can only see the password
hash if you&rsquo;re running Puppet with sufficient privileges). You can even
query <code>puppet resource</code> for the values of a specific resource:</p>

<pre><code>[root@gary ~]# puppet resource user glarizza
user { 'glarizza':
  ensure           =&gt; 'present',
  gid              =&gt; '502',
  home             =&gt; '/home/glarizza',
  password         =&gt; '$1$hsUuCygh$kgLKG5epuRaXHMX5KmxrL1',
  password_max_age =&gt; '99999',
  password_min_age =&gt; '0',
  shell            =&gt; '/bin/bash',
  uid              =&gt; '502',
}
</code></pre>

<p><code>puppet resource</code> seems magical, and you might think that if you create a
custom type and sync it to your machine then <code>puppet resource</code> will automatically
work for you.</p>

<p><strong>And you would be wrong.</strong></p>

<p><code>puppet resource</code> will only work if you&rsquo;ve implemented a special method in your
provider called <code>self.instances</code>.</p>

<h2><code>self.instances</code></h2>

<p>The <code>self.instances</code> method is pretty sparsely documented, so let&rsquo;s go straight
to the source&hellip;code, that is:</p>

<figure class='code'><figcaption><span>lib/puppet/provider.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>
<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'>  <span class="c1"># Returns a list of system resources (entities) this provider may/can manage.</span>
</span><span class='line'>  <span class="c1"># This is a query mechanism that lists entities that the provider may manage on a given system. It is</span>
</span><span class='line'>  <span class="c1"># is directly used in query services, but is also the foundation for other services; prefetching, and</span>
</span><span class='line'>  <span class="c1"># purging.</span>
</span><span class='line'>  <span class="c1">#</span>
</span><span class='line'>  <span class="c1"># As an example, a package provider lists all installed packages. (In contrast, the File provider does</span>
</span><span class='line'>  <span class="c1"># not list all files on the file-system as that would make execution incredibly slow). An implementation</span>
</span><span class='line'>  <span class="c1"># of this method should be made if it is possible to quickly (with a single system call) provide all</span>
</span><span class='line'>  <span class="c1"># instances.</span>
</span><span class='line'>  <span class="c1">#</span>
</span><span class='line'>  <span class="c1"># An implementation of this method should only cache the values of properties</span>
</span><span class='line'>  <span class="c1"># if they are discovered as part of the process for finding existing resources.</span>
</span><span class='line'>  <span class="c1"># Resource properties that require additional commands (than those used to determine existence/identity)</span>
</span><span class='line'>  <span class="c1"># should be implemented in their respective getter method. (This is important from a performance perspective;</span>
</span><span class='line'>  <span class="c1"># it may be expensive to compute, as well as wasteful as all discovered resources may perhaps not be managed).</span>
</span><span class='line'>  <span class="c1">#</span>
</span><span class='line'>  <span class="c1"># An implementation may return an empty list (naturally with the effect that it is not possible to query</span>
</span><span class='line'>  <span class="c1"># for manageable entities).</span>
</span><span class='line'>  <span class="c1">#</span>
</span><span class='line'>  <span class="c1"># By implementing this method, it is possible to use the `resources´ resource type to specify purging</span>
</span><span class='line'>  <span class="c1"># of all non managed entities.</span>
</span><span class='line'>  <span class="c1">#</span>
</span><span class='line'>  <span class="c1"># @note The returned instances are instance of some subclass of Provider, not resources.</span>
</span><span class='line'>  <span class="c1"># @return [Array&lt;Puppet::Provider&gt;] a list of providers referencing the system entities</span>
</span><span class='line'>  <span class="c1"># @abstract this method must be implemented by a subclass and this super method should never be called as it raises an exception.</span>
</span><span class='line'>  <span class="c1"># @raise [Puppet::DevError] Error indicating that the method should have been implemented by subclass.</span>
</span><span class='line'>  <span class="c1"># @see prefetch</span>
</span><span class='line'>  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">instances</span>
</span><span class='line'>    <span class="k">raise</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:DevError</span><span class="p">,</span> <span class="s2">&quot;Provider </span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> has not defined the &#39;instances&#39; class method&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>You&rsquo;ll find that method around lines 348 &ndash; 377 of the <code>lib/puppet/provider.rb</code>
file in Puppet&rsquo;s source code (as of this writing, which is a Friday&hellip;
on a flight from DC to Seattle). To summarize, implementing <code>self.instances</code>
in your provider means that you need to return an array of provider instances
that have been discovered on the current system and all the current property
values (we call these values the &lsquo;is&rsquo; values for the properties, since each
value IS the current value of the property on the system). It&rsquo;s recommended to only
implement <code>self.instances</code> if you can gather all resource property values in a reasonably
&lsquo;cheap&rsquo; manner (i.e. a single system call, read from a single file, or some
similar low-IO means). Implementing <code>self.instances</code> not only gives you the
ability to run <code>puppet resource</code> (which also affords you a quick-and-dirty
way of testing your provider without creating unit tests by simply running
<code>puppet resource</code> in debug mode and checking the output), but it also allows
the <a href="http://docs.puppetlabs.com/references/latest/type.html#resources">&lsquo;resources&rsquo; resource</a> to work its magic (If you&rsquo;ve
never heard of the &lsquo;resources&rsquo; resource, <a href="http://docs.puppetlabs.com/references/latest/type.html#resources">check this link</a>
for more information on this terribly/awesomely named resource type).</p>

<h3>An important note about scope and <code>self.instances</code></h3>

<p>The <code>self.instances</code> method is a method of the PROVIDER, which is why it is
prefixed with <code>self.</code>  Even though it may be located in the provider file
itself, and even though it sits among other methods like <code>create</code>, <code>exists?</code>,
and <code>destroy</code> (which are methods of the INSTANCE of the provider), it does
NOT have the ability to directly access or call those methods. It DOES have
the ability to access other methods of the provider directly (i.e. other
methods prefixed with <code>self.</code>).  This means that if you were to define a
method like:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">proxy_type</span>
</span><span class='line'>  <span class="s1">&#39;web&#39;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>You could access that directly from <code>self.instances</code> by simply calling it:</p>

<figure class='code'><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='ruby'><span class='line'><span class="n">type_of_proxy</span> <span class="o">=</span> <span class="n">proxy_type</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>Let&rsquo;s say you had a method of the INSTANCE of the provider, like so:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">system_type</span>
</span><span class='line'>  <span class="s1">&#39;OS X&#39;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>You COULD NOT access this method from <code>self.instances</code> directly (there are
always hacky ways around EVERYTHING in Ruby, sure, but there is no easy/straightforward
way to access this method).</p>

<p><strong>And here&rsquo;s where it gets confusing&hellip;</strong></p>

<p>Methods of the INSTANCE of the provider CAN access provider methods directly.
Given our previous example, what if the <code>system_type</code> method wanted to access
<code>self.proxy_type</code> for some reason?  It could be done like so:</p>

<figure class='code'><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='ruby'><span class='line'><span class="k">def</span> <span class="nf">system_type</span>
</span><span class='line'>  <span class="n">type_of_proxy</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">proxy_type</span><span class="p">()</span>
</span><span class='line'>  <span class="s1">&#39;OS X&#39;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>A method of the instance of the provider can access provider methods by simply
calling the <code>class</code> method on itself (which returns the provider object). This
is a one-way street for method creation that needs to be heeded when designing
your provider.</p>

<h2>Building a provider that uses <code>self.instances</code> (or: more Mac problems)</h2>

<p>In the previous two posts on types/providers, I created a type and provider
for managing bypass domains for network proxies on OS X. For this post, let&rsquo;s
create a provider for actually MANAGING the proxy settings for a given
network interface.  Here&rsquo;s a quick type for managing a web proxy on a network
interface on OS X:</p>

<figure class='code'><figcaption><span>puppet-mac_proxy/lib/puppet/type/mac_web_proxy.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>
<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">newtype</span><span class="p">(</span><span class="ss">:mac_web_proxy</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">desc</span> <span class="s2">&quot;Puppet type that models a network interface on OS X&quot;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">ensurable</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:namevar</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Interface name - currently must be &#39;friendly&#39; name (e.g. Ethernet)&quot;</span>
</span><span class='line'>    <span class="n">munge</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
</span><span class='line'>      <span class="n">value</span><span class="o">.</span><span class="n">downcase</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>    <span class="k">def</span> <span class="nf">insync?</span><span class="p">(</span><span class="n">is</span><span class="p">)</span>
</span><span class='line'>      <span class="n">is</span><span class="o">.</span><span class="n">downcase</span> <span class="o">==</span> <span class="n">should</span><span class="o">.</span><span class="n">downcase</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="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_server</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:authenticated_username</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Username for proxy authentication&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:authenticated_password</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Password for proxy authentication&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_authenticated</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>    <span class="n">newvalues</span><span class="p">(</span><span class="ss">:true</span><span class="p">,</span> <span class="ss">:false</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_port</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>    <span class="n">newvalues</span><span class="p">(</span><span class="sr">/^\d+$/</span><span class="p">)</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>This type has three properties, is ensurable, and a namevar called &lsquo;name&rsquo;. As
for the provider, let&rsquo;s start with <code>self.instances</code> and get the web proxy
values for all interfaces.  To do that we&rsquo;re going to need to know how to get
a list of all network interfaces, and also how to get the current proxy state
for every interface. Fortunately, both of those tasks are accomplished with the
<code>networksetup</code> binary:</p>

<pre><code>▷ networksetup -listallnetworkservices
An asterisk (*) denotes that a network service is disabled.
Bluetooth DUN
Display Ethernet
Ethernet
FireWire
Wi-Fi
iPhone USB
Bluetooth PAN

▷ networksetup -getwebproxy Ethernet
Enabled: No
Server: proxy.corp.net
Port: 1234
Authenticated Proxy Enabled: 0
</code></pre>

<p>Cool, so one binary will do both tasks and they&rsquo;re REASONABLY low-cost to run.</p>

<h3>Helper methods</h3>

<p>To keep things separated and easier to test, let&rsquo;s create separate helper methods
for each task. Since these methods are going to be called by <code>self.instances</code>,
they will be provider methods.</p>

<p>The first method will simply return an array of network interfaces:</p>

<figure class='code'><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="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get_list_of_interfaces</span>
</span><span class='line'>  <span class="n">interfaces</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="s1">&#39;-listallnetworkservices&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="n">interfaces</span><span class="o">.</span><span class="n">shift</span>
</span><span class='line'>  <span class="n">interfaces</span><span class="o">.</span><span class="n">sort</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Remember from above that the <code>networksetup -listallnetworkservices</code> command
returns an info line before each interface, so this code strips that line
off and returns a sorted list of interfaces based on a one-line-per-interface
assumption.</p>

<p>The next method we need will accept a network interface name as an argument,
will run the <code>networksetup -getwebproxy (interface)</code> command, and will use
its output to return all the current property values (including the ensure
value) for every instance of the type on the system (i.e. every interface&rsquo;s
proxy settings and whether the proxy is enabled, which means the resource
is ensured as &lsquo;present&rsquo;, or disabled, which means the resource is ensured
as &lsquo;absent&rsquo;.</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get_proxy_properties</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>  <span class="n">interface_properties</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">begin</span>
</span><span class='line'>    <span class="n">output</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-getwebproxy&#39;</span><span class="p">,</span> <span class="n">int</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="k">rescue</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:ExecutionFailure</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span><span class='line'>    <span class="k">raise</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Error</span><span class="p">,</span> <span class="s2">&quot;#mac_web_proxy tried to run `networksetup -getwebproxy </span><span class="si">#{</span><span class="n">int</span><span class="si">}</span><span class="s2">` and the command returned non-zero. Failing here...&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">output_array</span> <span class="o">=</span> <span class="n">output</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="n">output_array</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
</span><span class='line'>    <span class="n">line_values</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;:&#39;</span><span class="p">)</span>
</span><span class='line'>    <span class="n">line_values</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">strip!</span>
</span><span class='line'>    <span class="k">case</span> <span class="n">line_values</span><span class="o">.</span><span class="n">first</span>
</span><span class='line'>    <span class="k">when</span> <span class="s1">&#39;Enabled&#39;</span>
</span><span class='line'>      <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;No&#39;</span> <span class="p">?</span> <span class="ss">:absent</span> <span class="p">:</span> <span class="ss">:present</span>
</span><span class='line'>    <span class="k">when</span> <span class="s1">&#39;Server&#39;</span>
</span><span class='line'>      <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">empty?</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span>
</span><span class='line'>    <span class="k">when</span> <span class="s1">&#39;Port&#39;</span>
</span><span class='line'>      <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span>
</span><span class='line'>    <span class="k">when</span> <span class="s1">&#39;Authenticated Proxy Enabled&#39;</span>
</span><span class='line'>      <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_authenticated</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</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="n">interface_properties</span><span class="o">[</span><span class="ss">:provider</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:ruby</span>
</span><span class='line'>  <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span>     <span class="o">=</span> <span class="n">int</span><span class="o">.</span><span class="n">downcase</span>
</span><span class='line'>  <span class="n">interface_properties</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>A couple of notes on the method itself &ndash; first, the <code>networksetup</code> command must
exit zero on success or non-zero on failure (which it does). If ever the <code>networksetup</code>
command were to return non-zero, we&rsquo;re raising our own Puppet::Error, documenting what
happened, and bailing out.</p>

<p>This method is going to return a hash of properties and values that is going
to be used by <code>self.instances</code> &ndash; so the case statement needs to account for that.
HOWEVER you populate that hash is up to you (in my case, I&rsquo;m checking for specific
output that <code>networksetup</code> returns), but make sure that the hash has a value for
the <code>:ensure</code> key at the VERY least.</p>

<h3>Assembling <code>self.instances</code></h3>

<p>Once the helper provider methods have been defined, <code>self.instances</code> becomes
reasonably simple:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">instances</span>
</span><span class='line'>  <span class="n">get_list_of_interfaces</span><span class="o">.</span><span class="n">collect</span> <span class="k">do</span> <span class="o">|</span><span class="n">int</span><span class="o">|</span>
</span><span class='line'>    <span class="n">proxy_properties</span> <span class="o">=</span> <span class="n">get_proxy_properties</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>    <span class="kp">new</span><span class="p">(</span><span class="n">proxy_properties</span><span class="p">)</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>Remember that <code>self.instances</code> must return an array of provider instances,
and each one of these instances must include the <code>namevar</code> and ensure value
at the very least. Since <code>self.get_proxy_properties</code> returns a hash containing
all the property &lsquo;is&rsquo; values for a resource, declaring a new provider instance
is as easy as calling the <code>new()</code> method on the return value of
<code>self.get_proxy_properties</code> for every network interface. In the end, the
return value of the <code>collect</code> method on <code>get_list_of_interfaces</code> will be an
array of provider instances.</p>

<h2>Existance, <code>@property_hash</code>, and more magical methods</h2>

<p>Even though we have assembled a functional <code>self.instances</code> method, we don&rsquo;t have
complete implementation that will work with <code>puppet resource</code>.  The problem is
that Puppet can&rsquo;t yet determine the existance of a resource (even though the
resource&rsquo;s <code>ensure</code> value has been set by <code>self.instances</code>). If you were to
execute the code with <code>puppet resource mac_web_proxy</code>, you would get the error:</p>

<pre><code>Error: Could not run: No ability to determine if mac_web_proxy exists
</code></pre>

<p>To satisfy Puppet, we need to implement an <code>exists?()</code> method for the instance
of the provider. Fortunately, we don&rsquo;t need to re-implement any existing logic
and can instead use <code>@property_hash</code></p>

<h3>A <code>@property_hash</code> is born&hellip;</h3>

<p>I&rsquo;ve omitted one last thing that is borne out of <code>self.instances</code>, and that&rsquo;s
the <code>@property_hash</code> instance variable. <code>@property_hash</code> is populated by
<code>self.instances</code> as an instance variable that&rsquo;s available to methods of the
INSTANCE of the provider (i.e. methods that ARE NOT prefixed with <code>self.</code>)
containing all the &lsquo;is&rsquo; values for a resource. Do you need to get the &lsquo;is&rsquo;
value for a property?  Just use <code>@property_hash[:property_name]</code>. Since the
<code>exists?</code> method is a method of the instance of the provider, and it&rsquo;s
essentially the same thing as the <code>ensure</code> value for a resource, let&rsquo;s implement
<code>exists?</code> by doing a check on the ensure value from the <code>@property_hash</code>
variable:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">exists?</span>
</span><span class='line'>  <span class="vi">@property_hash</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">==</span> <span class="ss">:present</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Perfect, now <code>exists?</code> will return true or false accordingly and Puppet will be
satisfied.</p>

<h3>Getter methods &ndash; the slow way</h3>

<p>Puppet may be happy that you have an <code>exists?</code> method, but <code>puppet resource</code>
won&rsquo;t successfully run until you have a method that returns an &lsquo;is&rsquo; value for
every property of the type (i.e. the <code>proxy_server</code>, <code>proxy_authenticated</code>,
and <code>proxy_port</code> attributes for the mac_web_proxy type). These &lsquo;is value methods&rsquo; are
called &lsquo;getter&rsquo; methods: they&rsquo;re methods of the instance of the provider, and
are named exactly the same as the properties they represent.</p>

<p>You SHOULD be thinking: &ldquo;Hey, we already have <code>@property_hash</code>, why can&rsquo;t we
just use it again? We can, and you COULD implement all the getter methods
like so:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">proxy_server</span>
</span><span class='line'>  <span class="vi">@property_hash</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>If you did that, you would be TECHNICALLY correct, but it would seem to be a
waste of lines in a provider (especially if you have many properties).</p>

<h3>Getter methods &ndash; the quicker &lsquo;method&rsquo;</h3>

<p>Because uncle Luke hated excess lines of code, he made available a method called
<code>mk_resource_methods</code> which works very similarly to Ruby&rsquo;s <code>attr_accessor</code>
method. Adding <code>mk_resource_methods</code> to your provider will AUTOMATICALLY
create getter methods that pull values out of <code>@property_hash</code> in the similar
way that I just demonstrated (it will also create SETTER methods too, but we&rsquo;ll
look at those later). Long story short &ndash; don&rsquo;t make getter/setter methods if
you&rsquo;re using self.instances &ndash; just implement <code>mk_resource_methods</code>.</p>

<h2>JUST enough for <code>puppet resource</code></h2>

<p>Putting everything that we&rsquo;ve learned up until now, we should have a provider
that looks like this:</p>

<figure class='code'><figcaption><span>lib/puppet/provider/mac_web_proxy/ruby.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>
<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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">type</span><span class="p">(</span><span class="ss">:mac_web_proxy</span><span class="p">)</span><span class="o">.</span><span class="n">provide</span><span class="p">(</span><span class="ss">:ruby</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">commands</span> <span class="ss">:networksetup</span> <span class="o">=&gt;</span> <span class="s1">&#39;networksetup&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">mk_resource_methods</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">get_list_of_interfaces</span>
</span><span class='line'>    <span class="n">interfaces</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="s1">&#39;-listallnetworkservices&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="n">interfaces</span><span class="o">.</span><span class="n">shift</span>
</span><span class='line'>    <span class="n">interfaces</span><span class="o">.</span><span class="n">sort</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">get_proxy_properties</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>    <span class="n">interface_properties</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">begin</span>
</span><span class='line'>      <span class="n">output</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-getwebproxy&#39;</span><span class="p">,</span> <span class="n">int</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>    <span class="k">rescue</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:ExecutionFailure</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span><span class='line'>      <span class="no">Puppet</span><span class="o">.</span><span class="n">debug</span> <span class="s2">&quot;#get_proxy_properties had an error -&gt; </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'>      <span class="k">return</span> <span class="p">{}</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">output_array</span> <span class="o">=</span> <span class="n">output</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="n">output_array</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
</span><span class='line'>      <span class="n">line_values</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;:&#39;</span><span class="p">)</span>
</span><span class='line'>      <span class="n">line_values</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">strip!</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">line_values</span><span class="o">.</span><span class="n">first</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Enabled&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;No&#39;</span> <span class="p">?</span> <span class="ss">:absent</span> <span class="p">:</span> <span class="ss">:present</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Server&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">empty?</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Port&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Authenticated Proxy Enabled&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_authenticated</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</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="n">interface_properties</span><span class="o">[</span><span class="ss">:provider</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:ruby</span>
</span><span class='line'>    <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span>     <span class="o">=</span> <span class="n">int</span><span class="o">.</span><span class="n">downcase</span>
</span><span class='line'>    <span class="no">Puppet</span><span class="o">.</span><span class="n">debug</span> <span class="s2">&quot;Interface properties: </span><span class="si">#{</span><span class="n">interface_properties</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'>    <span class="n">interface_properties</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">instances</span>
</span><span class='line'>    <span class="n">get_list_of_interfaces</span><span class="o">.</span><span class="n">collect</span> <span class="k">do</span> <span class="o">|</span><span class="n">int</span><span class="o">|</span>
</span><span class='line'>      <span class="n">proxy_properties</span> <span class="o">=</span> <span class="n">get_proxy_properties</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>      <span class="kp">new</span><span class="p">(</span><span class="n">proxy_properties</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="k">def</span> <span class="nf">exists?</span>
</span><span class='line'>    <span class="vi">@property_hash</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">==</span> <span class="ss">:present</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>Here&rsquo;s a tree of the module I&rsquo;ve assembled on my machine:</p>

<pre><code>└(~/src/puppet-mac_web_proxy)▷ tree .
.
└── lib
   └── puppet
       ├── provider
       │   └── mac_web_proxy
       │       └── ruby.rb
       └── type
           └── mac_web_proxy.rb
</code></pre>

<p>To test out <code>puppet resource</code>, we need to make Puppet aware of our new custom
module. To do that, let&rsquo;s set the <code>$RUBYLIB</code> environmental variable. <code>$RUBYLIB</code>
is queried by Puppet and is added to its load path when looking for additional
Puppet plugins.  You will need to set <code>$RUBYLIB</code> to the path of the <code>lib</code> directory
in the custom module that you&rsquo;ve assembled.  Because my custom module is located
in <code>~/src/puppet-mac_web_proxy</code>, I&rsquo;m going to set <code>$RUBYLIB</code> like so:</p>

<pre><code>export RUBYLIB=~/src/puppet-mac_web_proxy/lib
</code></pre>

<p>You can execute that command from the command line, or set it in your
<code>~/.{bash,zsh}rc</code> and <code>source</code> that file.</p>

<p>Finally, with all the files in place and <code>$RUBYLIB</code> set, it&rsquo;s time to officially
run <code>puppet resource</code> (I&rsquo;m going to do it in <code>--debug</code> mode to see the debug output
that I&rsquo;ve written into the code):</p>

<pre><code>└(~/src/blogtests)▷ envpuppet puppet resource mac_web_proxy --debug
Debug: Executing '/usr/sbin/networksetup -listallnetworkservices'
Debug: Executing '/usr/sbin/networksetup -getwebproxy Bluetooth DUN'
Debug: Interface properties: {:ensure=&gt;:absent, :proxy_server=&gt;nil, :proxy_port=&gt;nil, :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"bluetooth dun"}
Debug: Executing '/usr/sbin/networksetup -getwebproxy Bluetooth PAN'
Debug: Interface properties: {:ensure=&gt;:absent, :proxy_server=&gt;nil, :proxy_port=&gt;nil, :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"bluetooth pan"}
Debug: Executing '/usr/sbin/networksetup -getwebproxy Display Ethernet'
Debug: Interface properties: {:ensure=&gt;:absent, :proxy_server=&gt;"foo.bar.baz", :proxy_port=&gt;"80", :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"display ethernet"}
Debug: Executing '/usr/sbin/networksetup -getwebproxy Ethernet'
Debug: Interface properties: {:ensure=&gt;:absent, :proxy_server=&gt;"proxy.corp.net", :proxy_port=&gt;"1234", :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"ethernet"}
Debug: Executing '/usr/sbin/networksetup -getwebproxy FireWire'
Debug: Interface properties: {:ensure=&gt;:present, :proxy_server=&gt;"stuff.bar.blat", :proxy_port=&gt;"8190", :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"firewire"}
Debug: Executing '/usr/sbin/networksetup -getwebproxy Wi-Fi'
Debug: Interface properties: {:ensure=&gt;:absent, :proxy_server=&gt;nil, :proxy_port=&gt;nil, :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"wi-fi"}
Debug: Executing '/usr/sbin/networksetup -getwebproxy iPhone USB'
Debug: Interface properties: {:ensure=&gt;:absent, :proxy_server=&gt;nil, :proxy_port=&gt;nil, :proxy_authenticated=&gt;nil, :provider=&gt;:ruby, :name=&gt;"iphone usb"}
mac_web_proxy { 'bluetooth dun':
  ensure =&gt; 'absent',
}
mac_web_proxy { 'bluetooth pan':
  ensure =&gt; 'absent',
}
mac_web_proxy { 'display ethernet':
  ensure =&gt; 'absent',
}
mac_web_proxy { 'ethernet':
  ensure =&gt; 'absent',
}
mac_web_proxy { 'firewire':
  ensure       =&gt; 'present',
  proxy_port   =&gt; '8190',
  proxy_server =&gt; 'stuff.bar.blat',
}
mac_web_proxy { 'iphone usb':
  ensure =&gt; 'absent',
}
mac_web_proxy { 'wi-fi':
  ensure =&gt; 'absent',
}
</code></pre>

<p>Note that you will only see &lsquo;is&rsquo; values if you have a proxy set on any of your
network interfaces (obviously, if you&rsquo;ve not setup a proxy, then it will show
as &lsquo;absent&rsquo; on every interface. You can setup a proxy by opening System
Preferences, clicking on the Network icon, choosing an interface from the list
on the left, clicking the Advanced button in the lower right corner of the
window, clicking the &lsquo;Proxies&#8221; tab at the top of the window, clicking the
checkbox next to the &ldquo;Web Proxy (HTTP)&rdquo; choice, and entering a proxy URL and
port. NOW do you get why we automate this bullshit?).  Also, your list of
network interfaces may not match mine if you have more or less interfaces than
I do.</p>

<p>TADA! <code>puppet resource</code> WORKS! ISN&rsquo;T THAT AWESOME?! WHY AM I TYPING IN CAPS?!</p>

<h2>Prefetching, flushing, caching, and other hard shit</h2>

<p>Okay, so up until now we&rsquo;ve implemented one half of the equation &ndash; we can query
&lsquo;is&rsquo; values and <code>puppet resource</code> works. What about using this &lsquo;more efficient&rsquo;
method of getting values for a type on the OTHER end of the spectrum? What if
instead of calling setter methods one-by-one to set values for all resources
of a type in a catalog we had a way to do it all at once? Well, such a way
exists, and it&rsquo;s called the <code>flush</code> method&hellip;but we&rsquo;re getting slightly ahead
of ourselves. Before we get to flushing, we need to point out that <code>self.instances</code>
is ONLY used by <code>puppet resource</code> &ndash; THAT&rsquo;S IT (and it&rsquo;s only used by <code>self.instances</code>
when you GET values from the system, not when you SET values on the system&hellip;and if
you never knew that <code>puppet resource</code> could actually SET values on the system, well,
I guess you got another surprise today). If we want <code>puppet agent</code> or
<code>puppet apply</code> to use the behavior that <code>self.instances</code> implements, we need
to create another method: <code>self.prefetch</code></p>

<h3><code>self.prefetch</code></h3>

<p>If you thought <code>self.instances</code> didn&rsquo;t have much documentation, wait until
you see <code>self.prefetch</code>.  After wading the waters of <code>self.prefetch</code>, I&rsquo;m
PRETTY SURE its implementation might have come to uncle Luke after a long
night in Reed&rsquo;s chem lab where he might have accidently synthesized mescaline.</p>

<p>Let&rsquo;s look at the codebase:</p>

<figure class='code'><figcaption><span>lib/puppet/provider.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># @comment Document prefetch here as it does not exist anywhere else (called from transaction if implemented)</span>
</span><span class='line'><span class="c1"># @!method self.prefetch(resource_hash)</span>
</span><span class='line'><span class="c1"># @abstract A subclass may implement this - it is not implemented in the Provider class</span>
</span><span class='line'><span class="c1"># This method may be implemented by a provider in order to pre-fetch resource properties.</span>
</span><span class='line'><span class="c1"># If implemented it should set the provider instance of the managed resources to a provider with the</span>
</span><span class='line'><span class="c1"># fetched state (i.e. what is returned from the {instances} method).</span>
</span><span class='line'><span class="c1"># @param resources_hash [Hash&lt;{String =&gt; Puppet::Resource}&gt;] map from name to resource of resources to prefetch</span>
</span><span class='line'><span class="c1"># @return [void]</span>
</span><span class='line'><span class="c1"># @api public</span>
</span></code></pre></td></tr></table></div></figure>


<p>That&rsquo;s right, documentation for <code>self.prefetch</code> in the Puppet codebase is
9 lines of comments in <code>lib/puppet/provider.rb</code>, which is awesome. So when
is <code>self.prefetch</code> used to provide information to Puppet and when is <code>self.instances</code> used?</p>

<table>
<thead>
<tr>
<th></th>
<th align="center">Puppet Subcommand        </th>
<th align="center"> Provider Method       </th>
<th align="center"> Execution Mode  </th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td align="center"> puppet resource         </td>
<td align="center"> self.instances        </td>
<td align="center"> getting values</td>
</tr>
<tr>
<td></td>
<td align="center"> puppet resource         </td>
<td align="center"> self.prefetch         </td>
<td align="center"> setting values</td>
</tr>
<tr>
<td></td>
<td align="center"> puppet agent            </td>
<td align="center"> self.prefetch         </td>
<td align="center"> getting values</td>
</tr>
<tr>
<td></td>
<td align="center"> puppet agent            </td>
<td align="center"> self.prefetch         </td>
<td align="center"> setting values</td>
</tr>
<tr>
<td></td>
<td align="center"> puppet apply            </td>
<td align="center"> self.prefetch         </td>
<td align="center"> getting values</td>
</tr>
<tr>
<td></td>
<td align="center"> puppet apply            </td>
<td align="center"> self.prefetch         </td>
<td align="center"> setting values  </td>
</tr>
</tbody>
</table>


<p>.</p>

<p>This doesn&rsquo;t mean that <code>self.instances</code> is really only handy for <code>puppet
resource</code> &ndash; that&rsquo;s definitely not the case.  In fact, frequently you will find
that <code>self.instances</code> is used by <code>self.prefetch</code> to do some of the heavy
lifting.  Even though <code>self.prefetch</code> works VERY SIMILARLY to the way that
<code>self.instances</code> works for <code>puppet resource</code> (and by that I mean that it&rsquo;s
going to gather a list of instances of a type on the system, and it&rsquo;s also
going to populate <code>@property_hash</code> for <code>puppet apply</code>, <code>puppet agent</code>, and when
when <code>puppet resource</code> is setting values), it&rsquo;s not an exact one-for-one match
with <code>self.instances</code>.  The <code>self.prefetch</code> method for a type is called once
per run when Puppet encounters a resource of that type in the catalog. The
argument to <code>self.prefetch</code> is a hash of all managed resources of that type
that are encountered in a compiled catalog for that node (the hash&rsquo;s key will
be the <code>namevar</code> of the resource, and the value will be an instance of
<code>Puppet::Type</code> &ndash; in this case, <code>Puppet::Type::Mac_web_proxy</code>).  Your task is to
implement a <code>self.prefetch</code> method that gets an array of instances of the
provider that are discovered on the system, iterates through the hash passed to
<code>self.prefetch</code> (containing all the resources of the type that were discovered
in the catalog), and passes the correct instance of the provider that was
discovered on the system to the <code>provider=</code> method of the correct instance of
the type that was discovered in the catalog.</p>

<p><strong>What the actual fuck?!</strong></p>

<p>Okay, let&rsquo;s break that apart to try and discover exactly what&rsquo;s going on here.
Assume that I&rsquo;ve setup a proxy for the &lsquo;FireWire&rsquo; interface on my laptop, and
I want to try and manage that resource with <code>puppet apply</code> (i.e. something that
uses <code>self.prefetch</code>). The resource in the manifest used to manage the proxy
will look something like this:</p>

<figure class='code'><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='puppet'><span class='line'><span class="nc">mac_web_proxy</span> <span class="p">{</span> <span class="s1">&#39;firewire&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span>       <span class="p">=&gt;</span> <span class="s1">&#39;present&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">proxy_port</span>   <span class="p">=&gt;</span> <span class="s1">&#39;8080&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">proxy_server</span> <span class="p">=&gt;</span> <span class="s1">&#39;proxy.server.org&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>When <code>self.prefetch</code> is called by Puppet, it&rsquo;s going to be passed a hash looking
something like this:</p>

<figure class='code'><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='ruby'><span class='line'><span class="p">{</span> <span class="s2">&quot;firewire&quot;</span> <span class="o">=&gt;</span> <span class="no">Mac_web_proxy</span><span class="o">[</span><span class="n">firewire</span><span class="o">]</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Because only one resource is encountered in the catalog, only one key/value
pair shows up in the hash that&rsquo;s passed as the argument to <code>self.prefetch</code>.</p>

<p>The job of <code>self.prefetch</code> is to find the current state of <code>Mac_web_proxy['firewire']</code>
on the system, create a new instance of the <code>mac_web_proxy</code> provider that contains
the &lsquo;is&rsquo; values for the <code>Mac_web_proxy['firewire']</code> resource, and assign this provider instance as the value
of the <code>provider=</code> method to the instance of the mac_web_proxy TYPE that is the
VALUE of the &lsquo;firewire&rsquo; key of the hash that&rsquo;s passed to <code>self.prefetch</code>.</p>

<p><strong>No, really, that&rsquo;s what it&rsquo;s supposed to do. I&rsquo;m not even sure what&rsquo;s real anymore</strong></p>

<p>You&rsquo;ll remember that <code>self.instances</code> gives us an array of resources that were
discovered on the system, so we have THAT part of the implementation written. We also have the
hash of resources that were encountered in the catalog &ndash; so we have THAT
part done too. Our only job is to connect the dots (la la la la), programmatically
speaking.  This should just about do it:</p>

<figure class='code'><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='ruby'><span class='line'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">prefetch</span><span class="p">(</span><span class="n">resources</span><span class="p">)</span>
</span><span class='line'>  <span class="n">instances</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">prov</span><span class="o">|</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">resource</span> <span class="o">=</span> <span class="n">resources</span><span class="o">[</span><span class="n">prov</span><span class="o">.</span><span class="n">name</span><span class="o">]</span>
</span><span class='line'>      <span class="n">resource</span><span class="o">.</span><span class="n">provider</span> <span class="o">=</span> <span class="n">prov</span>
</span><span class='line'>    <span class="k">end</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>I want to make a confession right now &ndash; I&rsquo;ve only ever copied and pasted this
code into every provider I&rsquo;ve ever written that needed <code>self.prefetch</code> implemented.
It wasn&rsquo;t until someone actually asked me what it DID that I had to walk the path
of figuring out EXACTLY what it did. Based on the last couple of paragraphs &ndash; can
you blame me?</p>

<p>This code iterates through the array of resources returned by <code>self.instances</code>,
tries to assign a variable <code>resource</code> based on referencing a key in the
<code>resources</code> hash using the name of the resource (remember, <code>resources</code> is
a hash containing all resources in the catalog), and, if this assignment works
(i.e. it isn&rsquo;t <code>nil</code>, which is what happens when you reference a key in a Ruby
hash that doesn&rsquo;t exist), then we&rsquo;re calling the <code>provider=</code> method on the
instance of the type that was referenced in the <code>resources</code> hash, and passing
it the resource that was discovered on the system by <code>self.instances</code>.</p>

<p>Wow.</p>

<p>Why <strong>DID</strong> we do all of that?  We did it all for the <code>@provider_hash</code>. Doing
this will populate <code>@provider_hash</code> in all methods of the instance of the
provider (i.e. <code>exists?</code>, <code>create</code>, <code>destroy</code>, etc..) just like <code>self.instances</code>
did for <code>puppet resource</code>.</p>

<h2>Flush it; Ship it</h2>

<p>As I alluded to above, the opposite side of the coin to prefetching (which
is a way to query the state for all resources at once) is flushing (or
specifically the <code>flush</code> method). The <code>flush</code> method is called once per
resource whenever the &lsquo;is&rsquo; and &lsquo;should&rsquo; values for a property differ (and
synchronization needs to occur). The <code>flush</code> method <strong>does not take the place
of property setter methods</strong>, but, rather, is used in conjunction with them
to determine how to synchronize resource property values. In this vein, it&rsquo;s
a single trigger that can be used to set all property values for an individual
resource simultaneously.</p>

<p>There are a couple of strategies for implementing <code>flush</code>, but one of the more
popular ones in use is to create an instance variable that will hold values to
be synchronized, and then determine inside <code>flush</code> how best to make
as-few-as-possible calls to the system to synchronize all the property values
for an individual resource.</p>

<p>Our resource type is unique because the <code>networksetup</code> binary that we&rsquo;ll be
using to synchronize values allows us to set most every property value with
a single command. Because of this, we really only need that instance variable
for one property &ndash; the ensure value.  But let&rsquo;s start with the initialization
of that instance variable for the <code>flush</code> method:</p>

<figure class='code'><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='ruby'><span class='line'><span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="p">{})</span>
</span><span class='line'>  <span class="k">super</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span><span class='line'>  <span class="vi">@property_flush</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>The <code>initialize</code> method is magic to Ruby &ndash; it&rsquo;s invoked when you instantiate
a new object. In our case, we want to create a new instance variable &ndash;
<code>@property_flush</code> &ndash; that will be available to all methods of the instance of
the provider. This instance variable will be a hash and will contain all the
&lsquo;should&rsquo; values that will need to be synchronized for a resource. The <code>super</code>
method in Ruby sends a message to the parent of the current object, asking it
to invoke a method of the same name (e.g. <code>intialize</code>).  Basically, the
<code>initialize</code> method is doing the exact same thing as it has always done with
one exception &ndash; making the instance variable available to all methods of the
instance of the provider.</p>

<h3>The only &lsquo;setter&rsquo; method you need</h3>

<p>This provider is going to be unique not only because the <code>networksetup</code>
binary will set values for ALL properties, but because to change/set ANY
property values you have to change/set ALL the property values at the same
time. Typically, you&rsquo;ll see providers that will need to pass arguments to
a binary in order to set individual values.  For example, if you had a
binary <code>fooset</code> that took arguments of <code>--bar</code> and <code>--baz</code> to set values
respectively for <code>bar</code> and <code>baz</code> properties of a resource, you might see the following
setter and flush methods for <code>bar</code> and <code>baz</code>:</p>

<figure class='code'><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='ruby'><span class='line'><span class="k">def</span> <span class="nf">bar</span><span class="o">=</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span><span class='line'>  <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:bar</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</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="nf">baz</span><span class="o">=</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span><span class='line'>  <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:baz</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</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="nf">flush</span>
</span><span class='line'>  <span class="n">array_arguments</span> <span class="o">=</span> <span class="o">[]</span>
</span><span class='line'>  <span class="k">if</span> <span class="vi">@property_flush</span>
</span><span class='line'>    <span class="n">array_arguments</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;--bar&#39;</span> <span class="o">&lt;&lt;</span> <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:bar</span><span class="o">]</span> <span class="k">if</span> <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:bar</span><span class="o">]</span>
</span><span class='line'>    <span class="n">array_arguments</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;--baz&#39;</span> <span class="o">&lt;&lt;</span> <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:baz</span><span class="o">]</span> <span class="k">if</span> <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:baz</span><span class="o">]</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>  <span class="k">if</span> <span class="o">!</span> <span class="n">array_arguments</span><span class="o">.</span><span class="n">empty?</span>
</span><span class='line'>    <span class="n">fooset</span><span class="p">(</span><span class="n">array_arguments</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">)</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>That&rsquo;s not the case for <code>networksetup</code> &ndash; in fact, one of the ONLY places in our code
where we&rsquo;re going to throw a value inside <code>@property_flush</code> is going to be in
the <code>destroy</code> method. If our intention is to ensure a proxy absent (or, in
this case, disable the proxy for a network interface), then we can short-circuit
the method we&rsquo;re going to create to set proxy values by simply checking for
a value in <code>@property_flush[:ensure]</code>. Here&rsquo;s what the <code>destroy</code> method looks like:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">destroy</span>
</span><span class='line'>  <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:absent</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Next, we need a method that will set values for our proxy. This method will
handle all interaction to <code>networksetup</code>.  So, how do you set proxy values
with <code>networksetup</code>?</p>

<pre><code>networksetup -setwebproxy &lt;networkservice&gt; &lt;domain&gt; &lt;port number&gt; &lt;authenticated&gt; &lt;username&gt; &lt;password&gt;
</code></pre>

<p>The three properties to our <code>mac_web_proxy</code> type are <code>proxy_port</code>, <code>proxy_server</code>,
and <code>proxy_authenticated</code> which map to the &lsquo;&lt;port number>&rsquo;, &lsquo;&lt;domain>&rsquo;,
and &lsquo;&lt;authenticated>&rsquo; values in this command. To change any of these values
means we have to pass ALL of these values (again, which is why our <code>flush</code>
implementation may be unique from other <code>flush</code> implementations). Here&rsquo;s what
the <code>set_proxy</code> method looks like:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">set_proxy</span>
</span><span class='line'>  <span class="k">if</span> <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">==</span> <span class="ss">:absent</span>
</span><span class='line'>      <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setwebproxystate&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="s1">&#39;off&#39;</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>      <span class="k">return</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">].</span><span class="n">nil?</span> <span class="ow">or</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">].</span><span class="n">nil?</span><span class="p">)</span>
</span><span class='line'>    <span class="k">raise</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Error</span><span class="p">,</span> <span class="s2">&quot;Proxy types other than &#39;auto&#39; require both a proxy_server and proxy_port setting&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>  <span class="k">if</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_authenticated</span><span class="o">]</span> <span class="o">!=</span> <span class="ss">:true</span>
</span><span class='line'>    <span class="n">networksetup</span><span class="p">(</span>
</span><span class='line'>      <span class="o">[</span>
</span><span class='line'>        <span class="s1">&#39;-setwebproxy&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span>
</span><span class='line'>      <span class="o">]</span>
</span><span class='line'>    <span class="p">)</span>
</span><span class='line'>  <span class="k">else</span>
</span><span class='line'>    <span class="n">networksetup</span><span class="p">(</span>
</span><span class='line'>      <span class="o">[</span>
</span><span class='line'>        <span class="s1">&#39;-setwebproxy&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;on&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:authenticated_username</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">[</span><span class="ss">:authenticated_password</span><span class="o">]</span>
</span><span class='line'>      <span class="o">]</span>
</span><span class='line'>    <span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>  <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setwebproxystate&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="s1">&#39;on&#39;</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>This helper method does all the validation checks for required properties,
executes the correct command, and enables the proxy. Now, let&rsquo;s implement
<code>flush</code>:</p>

<figure class='code'><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='ruby'><span class='line'><span class="k">def</span> <span class="nf">flush</span>
</span><span class='line'>  <span class="n">set_proxy</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1"># Collect the resources again once they&#39;ve been changed (that way `puppet</span>
</span><span class='line'>  <span class="c1"># resource` will show the correct values after changes have been made).</span>
</span><span class='line'>  <span class="vi">@property_hash</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">get_proxy_properties</span><span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>The last line re-populates <code>@property_hash</code> with the current resource values,
and is necessary for <code>puppet resource</code> to return correct values after it
makes a change to a resource during a run.</p>

<h3>The final method</h3>

<p>We&rsquo;ve implemented logic to query the state of all resources, to prefetch those
states, to make changes to all properties at once, and to destroy a resource
if it exists, but we&rsquo;ve yet to implement logic to CREATE a resource if it
doesn&rsquo;t exist and it should.  Well, this is a bit of a lie &ndash; the logic is in
the code, but we don&rsquo;t have a <code>create</code> method, so Puppet&rsquo;s going to complain:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">create</span>
</span><span class='line'>  <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:present</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Technically, this method doesn&rsquo;t have to do a DAMN thing. Why? Remember how the
<code>flush</code> method is triggered when a resource&rsquo;s &lsquo;is&rsquo; values differ from its
&lsquo;should&rsquo; values? Also, remember how the <code>flush</code> method only calls the <code>set_proxy</code>
method? And, finally, remember how <code>set_proxy</code> only checks if
<code>@property_flush[:ensure] == :absent</code> (and if it doesn&rsquo;t, then it goes about
its merry way running <code>networksetup</code>)?  Right, well add these things up and
you&rsquo;ll realize that the <code>create</code> method is essentially meaningless based on our
implementation (but if you OMIT <code>create</code>, then Puppet&rsquo;s going to throw a shit-fit
in the shape of of a <code>Puppet::Error</code> exception):</p>

<pre><code>Error: /Mac_web_proxy[firewire]/ensure: change from absent to present failed: Could not set 'present' on ensure: undefined method `create' for Mac_web_proxy[firewire]:Puppet::Type::Mac_web_proxy
</code></pre>

<p>So make Puppet happy and write the goddamn <code>create</code> method, okay?</p>

<h2>The complete provider:</h2>

<p>Wow, that was a wild ride, huh?  If you&rsquo;ve been coding along, you should have
created a file that looks something like this:</p>

<figure class='code'><figcaption><span>lib/puppet/provider/mac_web_proxy/ruby.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>
<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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">type</span><span class="p">(</span><span class="ss">:mac_web_proxy</span><span class="p">)</span><span class="o">.</span><span class="n">provide</span><span class="p">(</span><span class="ss">:ruby</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">commands</span> <span class="ss">:networksetup</span> <span class="o">=&gt;</span> <span class="s1">&#39;networksetup&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">mk_resource_methods</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="p">{})</span>
</span><span class='line'>    <span class="k">super</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@property_flush</span> <span class="o">=</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">get_list_of_interfaces</span>
</span><span class='line'>    <span class="n">interfaces</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="s1">&#39;-listallnetworkservices&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="n">interfaces</span><span class="o">.</span><span class="n">shift</span>
</span><span class='line'>    <span class="n">interfaces</span><span class="o">.</span><span class="n">sort</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">get_proxy_properties</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>    <span class="n">interface_properties</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">begin</span>
</span><span class='line'>      <span class="n">output</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-getwebproxy&#39;</span><span class="p">,</span> <span class="n">int</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>    <span class="k">rescue</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:ExecutionFailure</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span><span class='line'>      <span class="no">Puppet</span><span class="o">.</span><span class="n">debug</span> <span class="s2">&quot;#get_proxy_properties had an error -&gt; </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'>      <span class="k">return</span> <span class="p">{}</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">output_array</span> <span class="o">=</span> <span class="n">output</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="n">output_array</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
</span><span class='line'>      <span class="n">line_values</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;:&#39;</span><span class="p">)</span>
</span><span class='line'>      <span class="n">line_values</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">strip!</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">line_values</span><span class="o">.</span><span class="n">first</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Enabled&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;No&#39;</span> <span class="p">?</span> <span class="ss">:absent</span> <span class="p">:</span> <span class="ss">:present</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Server&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">empty?</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Port&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span>
</span><span class='line'>      <span class="k">when</span> <span class="s1">&#39;Authenticated Proxy Enabled&#39;</span>
</span><span class='line'>        <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:proxy_authenticated</span><span class="o">]</span> <span class="o">=</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span> <span class="p">?</span> <span class="kp">nil</span> <span class="p">:</span> <span class="n">line_values</span><span class="o">.</span><span class="n">last</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="n">interface_properties</span><span class="o">[</span><span class="ss">:provider</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:ruby</span>
</span><span class='line'>    <span class="n">interface_properties</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span>     <span class="o">=</span> <span class="n">int</span><span class="o">.</span><span class="n">downcase</span>
</span><span class='line'>    <span class="no">Puppet</span><span class="o">.</span><span class="n">debug</span> <span class="s2">&quot;Interface properties: </span><span class="si">#{</span><span class="n">interface_properties</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'>    <span class="n">interface_properties</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">instances</span>
</span><span class='line'>    <span class="n">get_list_of_interfaces</span><span class="o">.</span><span class="n">collect</span> <span class="k">do</span> <span class="o">|</span><span class="n">int</span><span class="o">|</span>
</span><span class='line'>      <span class="n">proxy_properties</span> <span class="o">=</span> <span class="n">get_proxy_properties</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>      <span class="kp">new</span><span class="p">(</span><span class="n">proxy_properties</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="k">def</span> <span class="nf">create</span>
</span><span class='line'>    <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:present</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="nf">exists?</span>
</span><span class='line'>    <span class="vi">@property_hash</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">==</span> <span class="ss">:present</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="nf">destroy</span>
</span><span class='line'>    <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:absent</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">prefetch</span><span class="p">(</span><span class="n">resources</span><span class="p">)</span>
</span><span class='line'>    <span class="n">instances</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">prov</span><span class="o">|</span>
</span><span class='line'>      <span class="k">if</span> <span class="n">resource</span> <span class="o">=</span> <span class="n">resources</span><span class="o">[</span><span class="n">prov</span><span class="o">.</span><span class="n">name</span><span class="o">]</span>
</span><span class='line'>        <span class="n">resource</span><span class="o">.</span><span class="n">provider</span> <span class="o">=</span> <span class="n">prov</span>
</span><span class='line'>      <span class="k">end</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="k">def</span> <span class="nf">set_proxy</span>
</span><span class='line'>    <span class="k">if</span> <span class="vi">@property_flush</span><span class="o">[</span><span class="ss">:ensure</span><span class="o">]</span> <span class="o">==</span> <span class="ss">:absent</span>
</span><span class='line'>        <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setwebproxystate&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="s1">&#39;off&#39;</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>        <span class="k">return</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">].</span><span class="n">nil?</span> <span class="ow">or</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">].</span><span class="n">nil?</span><span class="p">)</span>
</span><span class='line'>      <span class="k">raise</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Error</span><span class="p">,</span> <span class="s2">&quot;Both the proxy_server and proxy_port parameters require a value.&quot;</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_authenticated</span><span class="o">]</span> <span class="o">!=</span> <span class="ss">:true</span>
</span><span class='line'>      <span class="n">networksetup</span><span class="p">(</span>
</span><span class='line'>        <span class="o">[</span>
</span><span class='line'>          <span class="s1">&#39;-setwebproxy&#39;</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span>
</span><span class='line'>        <span class="o">]</span>
</span><span class='line'>      <span class="p">)</span>
</span><span class='line'>    <span class="k">else</span>
</span><span class='line'>      <span class="n">networksetup</span><span class="p">(</span>
</span><span class='line'>        <span class="o">[</span>
</span><span class='line'>          <span class="s1">&#39;-setwebproxy&#39;</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_server</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:proxy_port</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>          <span class="s1">&#39;on&#39;</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:authenticated_username</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'>          <span class="n">resource</span><span class="o">[</span><span class="ss">:authenticated_password</span><span class="o">]</span>
</span><span class='line'>        <span class="o">]</span>
</span><span class='line'>      <span class="p">)</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>    <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setwebproxystate&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="s1">&#39;on&#39;</span><span class="o">]</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="nf">flush</span>
</span><span class='line'>    <span class="n">set_proxy</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1"># Collect the resources again once they&#39;ve been changed (that way `puppet</span>
</span><span class='line'>    <span class="c1"># resource` will show the correct values after changes have been made).</span>
</span><span class='line'>    <span class="vi">@property_hash</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">get_proxy_properties</span><span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">)</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>Undoubtedly there are better ways to write this Ruby code, no? Also, I&rsquo;m SURE
I have some errors/bugs in that code. It&rsquo;s those things that keep me in a job&hellip;</p>

<h2>Final Thoughts</h2>

<p>So, I write these posts not to belittle or mock anyone who works on Puppet
or wrote any of its implementation (except the amazing/terrifying bastard who
came up with <code>self.prefetch</code>). Anybody who contributes to open source and who
builds a tool to save some time for a bunch of sysadmins is fucking awesome
in my book.</p>

<p>No, I write these posts so that you can understand the <strong>&lsquo;WHY&rsquo;</strong> piece of the
puzzle. If you fuck up the <strong>&lsquo;HOW&rsquo;</strong> of the code, you can spend some time in
Google and IRB to figure it out, but if you don&rsquo;t understand the <strong>&lsquo;WHY&rsquo;</strong>
then you&rsquo;re probably not going to even bother.</p>

<p>Also, selfishly, I move from project to project so quickly that it&rsquo;s REALLY
easy to forget both why AND how I did what I did. Posts like these give me
someplace to point people when they ask me &ldquo;What&rsquo;s <code>self.prefetch</code>?&rdquo; that
ISN&rsquo;T just the source code or a liquor store.</p>

<p>This isn&rsquo;t the last post in the series, by the way. I haven&rsquo;t even TOUCHED
on writing unit tests for this code, so that&rsquo;s going to be a WHOLE other
piece altogether. Also, while this provider manages a <strong>WEB</strong> proxy for
a network interface, understand that there are MANY MORE kinds of proxies
for OS X network interfaces (including socks and gopher!). A future post
will show you how to refactor the above into a parent provider that can
be inherited to allow for code re-use among all the proxy providers that I
need to create.</p>

<p>As always, you&rsquo;re more than welcome to comment, ask questions, or simply bitch
at me both on this blog as well as <a href="http://www.twitter.com/glarizza">on Twitter: @glarizza</a>. Hopefully
this post helped you out and you learned a little bit more about how Puppet
providers do their dirty work&hellip;</p>

<p>Namaste, bitches.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[When to Hiera (Aka: How Do I Module?)]]></title>
    <link href="http://garylarizza.com/blog/2013/12/08/when-to-hiera/"/>
    <updated>2013-12-08T17:31:54-06:00</updated>
    <id>http://garylarizza.com/blog/2013/12/08/when-to-hiera</id>
    <content type="html"><![CDATA[<p>I&rsquo;m convinced that writing Puppet modules is the ultimate exercise in bikeshedding:
if it works, someone&rsquo;s probably going to tell you that you could have done it better,
if you&rsquo;re using the methods suggested today, they&rsquo;re probably going to be out-of-date
in about 6 months, and good luck writing something that someone else can use cleanly
without needing to change it.</p>

<p>I can help you with the last two.</p>

<h2>Data and Code Separation == bliss?</h2>

<p><a href="http://bit.ly/puppetdata">I wrote a blog post about 2 years ago</a> detailing why separating
your data from your Puppet code was a good idea. The idea is still valid, which means
it&rsquo;s probably one of the better ideas I&rsquo;ve ever stolen (Does anyone want any HD-DVDs?).
Hunter Haugen and I <a href="http://bit.ly/hierablog">put together a quick blog post on using Hiera</a>
to solve the data/code problem because there wasn&rsquo;t a great bit of documentation on Hiera
at that point in time. Since then, Hiera&rsquo;s been widely accepted as &ldquo;a good idea&rdquo; and is
in use in production Puppet environments around the world. In most every environment,
usage of Hiera by more than just one person eventually gives birth to the question
that inspired this post:</p>

<h4>&ldquo;What the hell does and does NOT belong in Hiera?&rdquo;</h4>

<h2>Puppet data models</h2>

<h3>The params class pattern</h3>

<p>Many Puppet modules out there since Puppet 2.6 have begun using this pattern:</p>

<figure class='code'><figcaption><span>puppetlabs-mysql/manifests/server.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">mysql::server</span> <span class="p">(</span>
</span><span class='line'>  <span class="nv">$config_file</span>             <span class="p">=</span> <span class="nv">$mysql::params::config_file</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$manage_config_file</span>      <span class="p">=</span> <span class="nv">$mysql::params::manage_config_file</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$old_root_password</span>       <span class="p">=</span> <span class="nv">$mysql::params::old_root_password</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$override_options</span>        <span class="p">=</span> <span class="p">{},</span>
</span><span class='line'>  <span class="nv">$package_ensure</span>          <span class="p">=</span> <span class="nv">$mysql::params::server_package_ensure</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$package_name</span>            <span class="p">=</span> <span class="nv">$mysql::params::server_package_name</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$purge_conf_dir</span>          <span class="p">=</span> <span class="nv">$mysql::params::purge_conf_dir</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$remove_default_accounts</span> <span class="p">=</span> <span class="ss">false</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$restart</span>                 <span class="p">=</span> <span class="nv">$mysql::params::restart</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$root_group</span>              <span class="p">=</span> <span class="nv">$mysql::params::root_group</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$root_password</span>           <span class="p">=</span> <span class="nv">$mysql::params::root_password</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$service_enabled</span>         <span class="p">=</span> <span class="nv">$mysql::params::server_service_enabled</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$service_manage</span>          <span class="p">=</span> <span class="nv">$mysql::params::server_service_manage</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$service_name</span>            <span class="p">=</span> <span class="nv">$mysql::params::server_service_name</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$service_provider</span>        <span class="p">=</span> <span class="nv">$mysql::params::server_service_provider</span><span class="p">,</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">  # Deprecated parameters</span>
</span><span class='line'>  <span class="nv">$enabled</span>                 <span class="p">=</span> <span class="ss">undef</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$manage_service</span>          <span class="p">=</span> <span class="ss">undef</span>
</span><span class='line'><span class="p">)</span> <span class="kd">inherits</span> <span class="nc">mysql::params</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Puppet goodness goes here</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>If you&rsquo;re not familiar, this is a Puppet class definition for <code>mysql::server</code> that has several parameters
defined and defaulted to values that come out of the <code>mysql::params</code> class.  The
<code>mysql::params</code> class looks a bit like this:</p>

<figure class='code'><figcaption><span>puppetlabs-mysql/manifests/params.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">mysql::params</span> <span class="p">{</span>
</span><span class='line'>  <span class="kr">case</span> <span class="nv">$::osfamily</span> <span class="p">{</span>
</span><span class='line'>    <span class="s1">&#39;RedHat&#39;</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="kr">if</span> <span class="nv">$::operatingsystem</span> <span class="o">==</span> <span class="s1">&#39;Fedora&#39;</span> <span class="o">and</span> <span class="p">(</span><span class="ss">is_integer</span><span class="p">(</span><span class="nv">$::operatingsystemrelease</span><span class="p">)</span> <span class="o">and</span> <span class="nv">$::operatingsystemrelease</span> <span class="o">&gt;=</span> <span class="ss">19</span> <span class="o">or</span> <span class="nv">$::operatingsystemrelease</span> <span class="o">==</span> <span class="s2">&quot;Rawhide&quot;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="nv">$client_package_name</span> <span class="o">=</span> <span class="s1">&#39;mariadb&#39;</span>
</span><span class='line'>        <span class="nv">$server_package_name</span> <span class="o">=</span> <span class="s1">&#39;mariadb-server&#39;</span>
</span><span class='line'>      } <span class="kr">else</span> <span class="p">{</span>
</span><span class='line'>        <span class="nv">$client_package_name</span> <span class="o">=</span> <span class="s1">&#39;mysql&#39;</span>
</span><span class='line'>        <span class="nv">$server_package_name</span> <span class="o">=</span> <span class="s1">&#39;mysql-server&#39;</span>
</span><span class='line'>      }
</span><span class='line'>      <span class="nv">$basedir</span>             <span class="o">=</span> <span class="s1">&#39;/usr&#39;</span>
</span><span class='line'>      <span class="nv">$config_file</span>         <span class="o">=</span> <span class="s1">&#39;/etc/my.cnf&#39;</span>
</span><span class='line'>      <span class="nv">$datadir</span>             <span class="o">=</span> <span class="s1">&#39;/var/lib/mysql&#39;</span>
</span><span class='line'>      <span class="nv">$log_error</span>           <span class="o">=</span> <span class="s1">&#39;/var/log/mysqld.log&#39;</span>
</span><span class='line'>      <span class="nv">$pidfile</span>             <span class="o">=</span> <span class="s1">&#39;/var/run/mysqld/mysqld.pid&#39;</span>
</span><span class='line'>      <span class="nv">$root_group</span>          <span class="o">=</span> <span class="s1">&#39;root&#39;</span>
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    <span class="s1">&#39;Debian&#39;</span><span class="p">:</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">      ## More parameters defined here</span>
</span><span class='line'>    }
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>This pattern puts all conditional logic for all the variables/parameters used
in the module inside one class &ndash; the <code>mysql::params</code> class.  It&rsquo;s called the
&lsquo;params class pattern&rsquo; because we suck at naming things.</p>

<h4>Pros:</h4>

<ul>
<li>All conditional logic is in a single class</li>
<li>You always know which class to seek out if you need to change any of the logic used to determine a variable&rsquo;s value</li>
<li>You can use the include function because parameters for each class will be defaulted to the values that came out of the params class</li>
<li>If you need to override the value of a particular parameter, you can still use the parameterized class declaration syntax to do so</li>
<li>Anyone using Puppet version 2.6 or higher can use it (i.e. anyone who&rsquo;s been using Puppet since about 2010).</li>
</ul>


<h5>Cons:</h5>

<ul>
<li>Conditional logic is repeated in every module</li>
<li>You will need to use inheritance to inherit parameter values in each subclass</li>
<li>It&rsquo;s another place to look if you ALSO use Hiera inside the module</li>
<li>Data is inside the manifest, so business logic is also inside params.pp</li>
</ul>


<h3>Hiera defaults pattern</h3>

<p>When Hiera hit the scene, one of the first things people tried to do was
to incorporate it into existing modules. The logic at that time was that
you could keep all parameter defaults inside Hiera, rid yourself of the
params class, and then just make Hiera calls out for your data. This
pattern looks like this:</p>

<figure class='code'><figcaption><span>puppetlabs-mysql/manifests/server.pp </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='puppet'><span class='line'><span class="kd">class</span> <span class="nc">mysql::server</span> <span class="p">(</span>
</span><span class='line'>  <span class="nv">$config_file</span>             <span class="p">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;mysql::params::config_file&#39;</span><span class="p">,</span> <span class="s1">&#39;default value&#39;</span><span class="p">),</span>
</span><span class='line'>  <span class="nv">$manage_config_file</span>      <span class="p">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;mysql::params::manage_config_file&#39;</span><span class="p">,</span> <span class="s1">&#39;default value&#39;</span><span class="p">),</span>
</span><span class='line'>  <span class="nv">$old_root_password</span>       <span class="p">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;mysql::params::old_root_password&#39;</span><span class="p">,</span> <span class="s1">&#39;default value&#39;</span><span class="p">),</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">  ## Repeat the above pattern</span>
</span><span class='line'><span class="p">)</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Puppet goodness goes here</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<h4>Pros:</h4>

<ul>
<li>All data is locked up in Hiera (and its multiple backends)</li>
<li>Default values can be provided if a Hiera lookup fails</li>
</ul>


<h4>Cons:</h4>

<ul>
<li>You need to have Hiera installed, enabled, and configured to use this pattern</li>
<li>All data, including non-business logic, is in Hiera</li>
<li>If you use the default value, data could either come from Hiera OR the default (multiple places to look when debugging)</li>
</ul>


<h3>Hybrid data model</h3>

<p>This pattern is for those people who want the portability of the params.pp class
combined with the power of Hiera. Because it&rsquo;s a hybrid, there are multiple ways
that people have set it up.  Here&rsquo;s a general example:</p>

<figure class='code'><figcaption><span>puppetlabs-mysql/manifests/server.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">mysql::server</span> <span class="p">(</span>
</span><span class='line'>  <span class="nv">$config_file</span>             <span class="p">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;mysql::params::config_file&#39;</span><span class="p">,</span> <span class="nv">$mysql::params::config_file</span><span class="p">),</span>
</span><span class='line'>  <span class="nv">$manage_config_file</span>      <span class="p">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;mysql::params::manage_config_file&#39;</span><span class="p">,</span> <span class="nv">$mysql::params::manage_config_file</span><span class="p">),</span>
</span><span class='line'>  <span class="nv">$old_root_password</span>       <span class="p">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;mysql::params::old_root_password&#39;</span><span class="p">,</span> <span class="nv">$mysql::params::old_root_password</span><span class="p">),</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">  ## Repeat the above pattern</span>
</span><span class='line'><span class="p">)</span> <span class="kd">inherits</span> <span class="nc">mysql::params</span> <span class="p">{</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Pros:</h4>

<ul>
<li>Data is sought from Hiera first and then defaulted back to the params class parameter</li>
<li>Keep non-business logic (i.e. OS specific data) in the params class and business logic in Hiera</li>
<li>Added benefits of both models</li>
</ul>


<h4>Cons:</h4>

<ul>
<li>Where did the variable get set &ndash; Hiera or the params class? Debugging can be hard</li>
<li>Requires Hiera to be setup to use the module</li>
<li>If you fudge a variable name in Hiera, you get the params class default &ndash; see Con #1</li>
</ul>


<h3>Hiera data bindings in Puppet 3.x.x</h3>

<p>In Puppet 3.0.0, there was a concept introduced called Data Bindings. This created
a federated data model automatically incorporating a Hiera lookup. Previously, the
order that Puppet would use to determine the value of a parameter was to first use
a value passed with the parameterized class declaration syntax (i.e. the below:).</p>

<figure class='code'><figcaption><span>parameterized class declaration </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;apache&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">package_name</span> <span class="p">=&gt;</span> <span class="s1">&#39;httpd&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>If a parameter was not passed with the parameterized class syntax (like the &lsquo;package_name&rsquo;
parameter above&#8217;), Puppet would then look for a default value inside the class definition
(i.e. the below:).</p>

<figure class='code'><figcaption><span>parameter default in a class definition </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='puppet'><span class='line'><span class="kd">class</span> <span class="nc">ntp</span> <span class="p">(</span>
</span><span class='line'>  <span class="nv">$ntpserver</span> <span class="p">=</span> <span class="s1">&#39;default.ntpserver.org&#39;</span>
</span><span class='line'><span class="p">)</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">  # Use $ntpserver in a file declaration...</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>If the value of &lsquo;ntpserver&rsquo; wasn&rsquo;t passed with a parameterized class declaration,
then the value would be set to &lsquo;default.ntpserver.org&rsquo;, since that&rsquo;s the default
set in the above class definition.</p>

<p>Failing both of these conditions, Puppet would throw a parse error and say
that it couldn&rsquo;t determine a value for a class parameter.</p>

<p>As of Puppet 3.0.0, Puppet will now do a Hiera lookup for the fully namespaced
value of a class parameter</p>

<h3>Roles and Profiles</h3>

<p><a href="http://sysadvent.blogspot.com/2012/12/day-13-configuration-management-as-legos.html">The roles and profiles pattern</a> has been written about
a number of times and is ALSO considered to be &lsquo;a best practice&rsquo; when setting
up your Puppet environment. What roles and profiles gets you is a &lsquo;wrapper
class&rsquo; that allows you to declare classes within this wrapper class:</p>

<figure class='code'><figcaption><span>profiles/manifests/wordpress.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">profiles::wordpress</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">  # Data Lookups</span>
</span><span class='line'>  <span class="nv">$site_name</span>               <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::site_name&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_user_password</span> <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_user_password&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$mysql_root_password</span>     <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::mysql_root_password&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_db_host</span>       <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_db_host&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_db_name</span>       <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_db_name&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="nv">$wordpress_db_password</span>   <span class="o">=</span> <span class="nf">hiera</span><span class="p">(</span><span class="s1">&#39;profiles::wordpress::wordpress_db_password&#39;</span><span class="p">)</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Create user</span>
</span><span class='line'>  <span class="nc">group</span> <span class="p">{</span> <span class="s1">&#39;wordpress&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="nc">user</span> <span class="p">{</span> <span class="s1">&#39;wordpress&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>   <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">gid</span>      <span class="p">=&gt;</span> <span class="s1">&#39;wordpress&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">password</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_user_password</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">home</span>     <span class="p">=&gt;</span> <span class="s1">&#39;/var/www/wordpress&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Configure mysql</span>
</span><span class='line'>  <span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;mysql::server&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">root_password</span> <span class="p">=&gt;</span> <span class="nv">$wordpress_root_password</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">class</span> <span class="p">{</span> <span class="s1">&#39;mysql::bindings&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">php_enable</span> <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ## Configure apache</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">apache</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">apache::mod::php</span>
</span><span class='line'>}<span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">## Continue with declarations...</span>
</span></code></pre></td></tr></table></div></figure>


<p>Notice that any variables that might have business specific logic are set with
Hiera lookups. These Hiera lookups do NOT have default values, which means the
<code>hiera()</code> function will throw a parse error if a value is not returned. This
is IDEAL because we WANT TO KNOW if a Hiera lookup fails &ndash; this means we failed
to put the data in Hiera and should be corrected BEFORE a state that might
contain invalid data is enforced with Puppet.</p>

<p>You then have a &lsquo;Role&rsquo; wrapper class that simply includes many of the &lsquo;Profile&rsquo;
wrapper classes:</p>

<figure class='code'><figcaption><span>roles/manifests/frontend.pp </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='puppet'><span class='line'><span class="kd">class</span> <span class="nc">roles::frontend</span> <span class="p">{</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">profiles::mysql</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">profiles::apache</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">profiles::java</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">profiles::jboss</span>
</span><span class='line'><span class="c-Singleline">  # include more profiles...</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>The idea being that Profiles abstract all the technical bits that need to
declared to setup a piece of technology, and Roles will abstract all the
business logic for what pieces of technology should be installed on a certain
&lsquo;class&rsquo; of machine.  Basically, you can say that &ldquo;all our frontend infrastructure
should have mysql, apache, java, jboss&hellip;&rdquo;.  In this statement, the Role is
&lsquo;frontend infrastructure&rsquo; and the Profiles are &lsquo;mysql, apache, java, jboss&hellip;&rsquo;.</p>

<h4>Pros:</h4>

<ul>
<li>Hiera data lookups are confined to a wrapper class OUTSIDE of the component modules (like mysql, apache, java, etc&hellip;)</li>
<li>Data lookups for parameters containing business logic are done with Hiera</li>
<li>Non-business specific data is pulled from the module (i.e. the params class)</li>
<li>Wrapper modules can be &lsquo;included&rsquo; with the <code>include</code> function, helping to eliminate multiple class declarations using the parameterized class declaration syntax</li>
<li>Component modules are backward-compatible to Puppet 2.6 while wrapper modules still get to use a modern data lookup mechanism (Hiera)</li>
<li>Component modules do NOT contain any business specific logic, which means they&rsquo;re portable</li>
</ul>


<h4>Cons:</h4>

<ul>
<li>Hiera must be setup to use the wrapper modules</li>
<li>Wrapper modules add another debug path for variable data</li>
<li>Wrapper modules add another layer of abstraction</li>
</ul>


<h2>Data in Puppet Modules</h2>

<p>R.I. Pienaar (the original author of MCollective, Hiera, and much more)
<a href="http://www.devco.net/archives/2013/12/08/better-puppet-modules-using-hiera-data.php">published a blog post</a>
recently on implementing a folder for Puppet modules that Hiera can traverse
when it does data lookups. This construct isn&rsquo;t new,
<a href="https://projects.puppetlabs.com/issues/16856">there was a feature request</a>
for this behavior filed in October of 2012 with a
<a href="https://github.com/puppetlabs/puppet/pull/1217">subsequent pull request</a>
that implemented this functionality (they&rsquo;re both worth reads for further
information). The pull request didn&rsquo;t get merged, and so R.I. implemented
the functionality <a href="http://forge.puppetlabs.com/ripienaar/module_data">inside a module on the Puppet Forge</a>.
In a nutshell, it&rsquo;s a hiera.yaml configuration file INSIDE THE MODULE that
implements a module-specific hierarchy, and a &lsquo;data&rsquo; folder (also inside
the module) that allows for individual YAML files that Hiera could read.
This hierarchy is consulted AFTER the site-specific <code>hiera.yaml</code> file
is read (i.e. <code>/etc/puppet/hiera.yaml</code> or <code>/etc/puppetlabs/puppet/hiera.yaml</code>),
and the in-module data files are consulted AFTER the site-specific Hiera
data files are read (normally found in either <code>/etc/puppet/hieradata</code> or
<code>/etc/puppetlabs/puppet/hieradata</code>).</p>

<p>The argument here is that there&rsquo;s a data store for <strong>SITE-SPECIFIC</strong> Hiera
data that should be kept outside of modules, but there&rsquo;s not a <strong>MODULE-SPECIFIC</strong>
data store that Hiera can use. The argument isn&rsquo;t whether data that should be
shared with other people belongs inside a site-specific Hiera datastore
(protip: it doesn&rsquo;t. Data that&rsquo;s not business-specific should be shared
with others and kept inside the module), the argument is that it shouldn&rsquo;t
be locked up inside the DSL where the barrier-to-entry is learning Puppet&rsquo;s
DSL syntax. Whereas <code>/etc/puppet/hiera.yaml</code> or <code>/etc/puppetlabs/puppet/hiera.yaml</code>
sets up the hierarchy for all your site-specific data, there&rsquo;s no per-module
<code>hiera.yaml</code> file for all module-specific data, and there&rsquo;s no place to put
module-specific Hiera data.</p>

<h4>But module-specific data goes inside the params class and business-specific data goes inside Hiera, right?</h4>

<p>Sure, but for some people the Puppet DSL is a barrier. The argument is that
there should be a lower barrier to entry to contribute parameter data
to Puppet that doesn&rsquo;t require you to learn the syntax of if/case/selector
statements in the Puppet DSL. There&rsquo;s also the argument that if you want
to add support for an operatingsystem to your module, you have to modify
the params class file and add another entry to the if/case/selector
statement &ndash; wouldn&rsquo;t it be easier to just add another YAML file into
a data folder that doesn&rsquo;t affect existing datafiles?</p>

<h4>Great, ANOTHER hierarchy to traverse for data &ndash; that&rsquo;s going to get confusing</h4>

<p>Well, think about it right now &ndash; most EVERY params class of EVERY module
(if it supports multiple operatingsystems)
does some sort of conditional logic to determine values for parameters
on a per-OS basis. That&rsquo;s something that you need to traverse. And many
modules use different conditional data to determine what paramters to use. Look
at the mysql params class example above &ndash; it not only splits on
<code>$osfamily</code>, but it also checks specific operatingsystems. That&rsquo;s a
conditional inside a conditional. You&rsquo;re TRAVERSING conditional data
right now to find a value &ndash; the only difference is that this method
doesn&rsquo;t use the DSL, it uses Hiera and YAML.</p>

<h4>Sure, but this is outside of Puppet and you&rsquo;re losing visibility inside Puppet with your data</h4>

<p>You&rsquo;re already doing that if you&rsquo;re using the params class. In this case, visibility is
moved to YAML files instead of separate Puppet classes.</p>

<h3>Setting it up</h3>

<p>You will first need to install R.I.&rsquo;s module from the Puppet Forge. As of
this writing, it&rsquo;s version <code>0.0.1</code>, so ensure you have the most recent
version using the <code>puppet module</code> tool:</p>

<pre><code>[root@linux modules]# puppet module install ripienaar/module_data
Notice: Preparing to install into /etc/puppetlabs/puppet/modules ...
Notice: Downloading from https://forge.puppetlabs.com ...
Notice: Installing -- do not interrupt ...
/etc/puppetlabs/puppet/modules
└── ripienaar-module_data (v0.0.1)
</code></pre>

<p>Next, you&rsquo;ll need to setup a module to use the data-in-modules pattern. Take
a look at the tree of a sample module:</p>

<pre><code>[root@linux modules]# tree mysql/
mysql/
├── data
│   ├── hiera.yaml
│   └── RedHat.yaml
└── manifests
    └── init.pp
</code></pre>

<p>I created a sample <code>mysql</code> module based on the examples above. All of
the module&rsquo;s Hiera data (including the module-specific hiera.yaml file)
goes in the <code>data</code> folder. This module should be placed in Puppet&rsquo;s
modulepath &ndash; and if you don&rsquo;t know where Puppet&rsquo;s modulepath is set,
run the <code>puppet config</code> face to determine that:</p>

<pre><code>[root@linux modules]# puppet config print modulepath
/etc/puppetlabs/puppet/modules:/opt/puppet/share/puppet/modules
</code></pre>

<p>In my case, I&rsquo;m putting the module in <code>/etc/puppetlabs/puppet/modules</code>
(since I&rsquo;m running Puppet Enterprise). Here&rsquo;s the hiera.yaml file
from the sample mysql module:</p>

<figure class='code'><figcaption><span>mysql/data/hiera.yaml </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='yaml'><span class='line'><span class="l-Scalar-Plain">:hierarchy</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{::osfamily}&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>I&rsquo;ve also included a YAML file for the <code>$osfamily</code> of RedHat:</p>

<figure class='code'><figcaption><span>mysql/data/RedHat.yaml </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='yaml'><span class='line'><span class="nn">---</span>
</span><span class='line'><span class="l-Scalar-Plain">mysql::config_file</span><span class="p-Indicator">:</span> <span class="s">&#39;/path/from/data_in_modules&#39;</span>
</span><span class='line'><span class="l-Scalar-Plain">mysql::manage_config_file</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">true</span>
</span><span class='line'><span class="l-Scalar-Plain">mysql::old_root_password</span><span class="p-Indicator">:</span> <span class="s">&#39;password_from_data_in_modules&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Finally, here&rsquo;s what the mysql class definition looks like from
<code>manifests/init.pp</code>:</p>

<figure class='code'><figcaption><span>mysql/manifests/init.pp </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='puppet'><span class='line'><span class="kd">class</span> <span class="nc">mysql</span> <span class="p">(</span>
</span><span class='line'>  <span class="nv">$config_file</span>        <span class="p">=</span> <span class="s1">&#39;module_default&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$manage_config_file</span> <span class="p">=</span> <span class="s1">&#39;module_default&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nv">$old_root_password</span>  <span class="p">=</span> <span class="s1">&#39;module_default&#39;</span>
</span><span class='line'><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="nc">notify</span> <span class="p">{</span> <span class="s2">&quot;The value of config_file: </span><span class="si">${config_file}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="p">}</span>
</span><span class='line'>  <span class="nc">notify</span> <span class="p">{</span> <span class="s2">&quot;The value of manage_config_file: </span><span class="si">${manage_config_file}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="p">}</span>
</span><span class='line'>  <span class="nc">notify</span> <span class="p">{</span> <span class="s2">&quot;The value of old_root_password: </span><span class="si">${old_root_password}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="p">}</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>Everything should be setup to notify the value of a couple of parameters.
Now, to test it out&hellip;</p>

<h3>Testing data-in-modules</h3>

<p>Let&rsquo;s include the mysql class with <code>puppet apply</code> to see where it&rsquo;s looking
for data:</p>

<pre><code>[root@linux modules]# puppet apply -e 'include mysql'
Notice: The value of config_file: /path/from/data_in_modules
Notice: /Stage[main]/Mysql/Notify[The value of config_file: /path/from/data_in_modules]/message: defined 'message' as 'The value of config_file: /path/from/data_in_modules'
Notice: The value of manage_config_file: true
Notice: /Stage[main]/Mysql/Notify[The value of manage_config_file: true]/message: defined 'message' as 'The value of manage_config_file: true'
Notice: The value of old_root_password: password_from_data_in_modules
Notice: /Stage[main]/Mysql/Notify[The value of old_root_password: password_from_data_in_modules]/message: defined 'message' as 'The value of old_root_password: password_from_data_in_modules'
Notice: Finished catalog run in 0.62 seconds
</code></pre>

<p>Since I&rsquo;m running on an operatingsystem whose family is &lsquo;RedHat&rsquo; (i.e. CentOS),
you can see that the values of all the parameters were pulled from the Hiera
data files inside the module.  Let&rsquo;s temporarily change the <code>$osfamily</code> fact
value and see what happens:</p>

<pre><code>[root@linux modules]# FACTER_osfamily=Debian puppet apply -e 'include mysql'
Notice: The value of config_file: module_default
Notice: /Stage[main]/Mysql/Notify[The value of config_file: module_default]/message: defined 'message' as 'The value of config_file: module_default'
Notice: The value of old_root_password: module_default
Notice: /Stage[main]/Mysql/Notify[The value of old_root_password: module_default]/message: defined 'message' as 'The value of old_root_password: module_default'
Notice: The value of manage_config_file: module_default
Notice: /Stage[main]/Mysql/Notify[The value of manage_config_file: module_default]/message: defined 'message' as 'The value of manage_config_file: module_default'
Notice: Finished catalog run in 0.51 seconds
</code></pre>

<p>This time, when I specified a value of <code>Debian</code> for <code>$osfamily</code>, the parameter
values were pulled from the declaration in the mysql class definition (i.e.
from inside <code>mysql/manifests/init.pp</code>).</p>

<h3>Testing outside of Puppet</h3>

<p>One of the big pros of Hiera is that it comes with the <code>hiera</code> binary that can
be run from the command line to test values. This works just fine for site-specific
module data that&rsquo;s defined in the central <code>hiera.yaml</code> file that&rsquo;s usually defined
in <code>/etc/puppet</code> or <code>/etc/puppetlabs/puppet</code>, but the data-in-modules pattern relies
on a Puppet indirector to point to the current module&rsquo;s <code>data</code> folder, and thus
(as of right now) there&rsquo;s not a simple way to run the <code>hiera</code> binary to pull
data out of modules WITHOUT running Puppet. This is not a dealbreaker, and
doesn&rsquo;t stop anybody from hacking up something that WILL look inside modules
for data, but as of right now it doesn&rsquo;t yet exist. It also makes debugging for
values that come out of modules a bit more difficult.</p>

<h3>The scorecard for data-in-modules</h3>

<h4>Pros:</h4>

<ul>
<li>Parameters are defined in YAML and not Puppet DSL (i.e. you only need to know YAML and not the Puppet DSL)</li>
<li>Adding parameters is as simple as adding another YAML file to the module</li>
<li>Module authors provide module data that can be read by Puppet 3.x.x Hiera data bindings</li>
</ul>


<h4>Cons:</h4>

<ul>
<li>Must be using Puppet 3.0.0 or higher</li>
<li>Additional hierarchy and additional Hiera data file to consult when debugging</li>
<li>Not (currently) an easy/straightforward way to use the <code>hiera</code> binary to test values</li>
<li>Currently depends on a Puppet Forge module being installed on your system</li>
</ul>


<h2>What are you trying to say?</h2>

<p>I am ALL ABOUT code portability, re-usability, and not building 500 apache modules.
Ever since people have been building modules, they&rsquo;ve been putting too much data
inside modules (to the point where they can&rsquo;t share them with anyone else). I
can&rsquo;t tell you how many times I&rsquo;ve heard &ldquo;We have a module for that, but I can&rsquo;t
share it because it has all our company-specific data in it.&rdquo;</p>

<p>Conversely, I&rsquo;ve also seen organizations put EVERYTHING in their site-specific
Hiera datastore because &ldquo;that&rsquo;s the place for Puppet data.&rdquo; They usually end up
with 15+ levels in their Hiera hierarchies because they&rsquo;re doing things like
this:</p>

<figure class='code'><figcaption><span>hiera.yaml </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>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="nn">---</span>
</span><span class='line'><span class="l-Scalar-Plain">:backends</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">yaml</span>
</span><span class='line'>
</span><span class='line'><span class="l-Scalar-Plain">:hierarchy</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{clientcert}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{environment}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{osfamily}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{osfamily}/%{operatingsystem}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{osfamily}/%{operatingsystem}/%{os_version_major}&quot;</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="s">&quot;%{osfamily}/%{operatingsystem}/%{os_version_minor}&quot;</span>
</span><span class='line'>  <span class="c1"># Repeat until you have 15 levels of WTF</span>
</span></code></pre></td></tr></table></div></figure>


<p>This leads us back again to &ldquo;What does and DOESN&rsquo;T go in Hiera?&rdquo;
I usually say the following:</p>

<h4>Data in site-specific Hiera datastore</h4>

<ul>
<li>Business-specific data (i.e. internal NTP server, VIP address, per-environment java application versions, etc&hellip;)</li>
<li>Sensitive data</li>
<li>Data that you don&rsquo;t want to share with anyone else</li>
</ul>


<h4>Data that does NOT go in the site-specific Hiera datastore</h4>

<ul>
<li>OS-specific data</li>
<li>Data that EVERYONE ELSE who uses this module will need to know (paths to config files, package names, etc&hellip;)</li>
</ul>


<p>Basically, if I ask you if I can publish your module to <a href="http://forge.puppetlabs.com">the Puppet Forge</a>,
and you object because it has business-specific or sensitive data in it, then
you probably need to pull that data out of the module and put it in Hiera.</p>

<p>The recommendations that I give when I go on-site with Puppet users is the following:</p>

<ul>
<li>Use Roles/Profiles to create wrapper-classes for class declaration</li>
<li>Do ALL Hiera lookups for site-specific data inside your &lsquo;Profile&rsquo; wrapper classes</li>
<li>All module-specific data (like paths to config files, names of packages to install, etc&hellip;) should be kept in the module in the params class</li>
<li>All &lsquo;Role&rsquo; wrapper classes should just <strong>include</strong> &lsquo;Profile&rsquo; wrapper classes &ndash; nothing else</li>
</ul>


<h2>But what about Data in Modules?</h2>

<p>I went through all the trouble of writing up the Data in Modules pattern,
but I didn&rsquo;t recommend or even MENTION it in the previous section. The reason
is NOT because I don&rsquo;t believe in it (I actually think the future will be data
outside of the DSL inside a Puppet module), the reason is because it&rsquo;s not <strong>YET</strong>
in Puppet&rsquo;s core and because it&rsquo;s not <strong>YET</strong> been widely tested. If you&rsquo;re an
existing Puppet user that&rsquo;s been looking for a way to split data outside of
the DSL, here is your opportunity. Use the pattern and PLEASE report back on what
you like and don&rsquo;t like about it. The functionality is in a module, so it&rsquo;s
easy to tweak. If you&rsquo;re new to Puppet and are comfortable with the DSL, then
the params class exists and is available to you.</p>

<p>To voice your opinion or to follow the progress of data in modules,
<a href="https://projects.puppetlabs.com/issues/16856">follow or comment on this Puppet ticket.</a></p>

<h2>Update</h2>

<p><a href="http://www.devco.net/archives/2013/12/09/the-problem-with-params-pp.php">R.I. posted another article on the problem with params.pp</a>
that is worth reading. He gives compelling reasons on why he built Hiera, why
params.pp WORKS, but also why he believes it&rsquo;s not the future of Puppet. R.I.
goes even further to explain that it&rsquo;s not necessarily the Puppet DSL that is
the barrier to entry, it&rsquo;s that this sort of data belongs in a file for config
data and not INSIDE THE CODE itself (i.e. inside the Puppet DSL). Providing data
inside modules gives module authors a way to provide this configuration data
in files that AREN&rsquo;T the Puppet DSL (i.e. not inside the code).</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Who Abstracted My Ruby?]]></title>
    <link href="http://garylarizza.com/blog/2013/11/26/fun-with-providers-part-2/"/>
    <updated>2013-11-26T16:01:00-06:00</updated>
    <id>http://garylarizza.com/blog/2013/11/26/fun-with-providers-part-2</id>
    <content type="html"><![CDATA[<p>Previously, on Lost, I said a lot of words about Puppet Types;
<a href="http://garylarizza.com/blog/2013/11/25/fun-with-providers/">you should totally check it out.</a>
In this second installment, you&rsquo;re going to find out how to actually throw
pure Ruby at Puppet in a way that makes you feel accomplished. And useful. And elitist.
Well, possibly just elitist. Either way, read on &ndash; there&rsquo;s much thought-leadership to be done&hellip;</p>

<p>In the last post, we learned that Types will essentially dictate the attributes
that you&rsquo;ll be passing in your resource declaration using the DSL. In the
simplest and crudest explanation I could muster, types model how your
declaration will look in the manifest. Providers are where the actual
IMPLEMENTATION happens. If you&rsquo;ve ever wondered how this:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="nc">package</span> <span class="p">{</span> <span class="s1">&#39;httpd&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">installed</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>eventually gets turned into this:</p>

<figure class='code'><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'>yum install -e 0 -d 0 -y httpd
</span></code></pre></td></tr></table></div></figure>


<p>your answer would be &ldquo;It&rsquo;s in the provider file&rdquo;.</p>

<h2>Dirty black magic</h2>

<p>I&rsquo;ve seen people do the craziest shit imaginable in the Puppet DSL simply because
they&rsquo;re:</p>

<ul>
<li>Unsure how types and providers work</li>
<li>Afraid of Ruby</li>
<li>Confused by error messages</li>
<li>Afraid to ask for help</li>
</ul>


<p>Sometimes you have a problem that can only be solved by interacting with data
that&rsquo;s returned by a binary (using some binary to get a value, and then using
that binary to set a value, and so on&hellip;). I see people writing defined resource
types with a SHIT TON of <code>exec</code> statements and conditional logic to model this
data when a type and provider would not only BETTER model the problem but would
also be shareable and re-useable by other folk. The issue is that while the DSL
is REALLY easy to get started with, types and providers still feel like dirty
black magic.</p>

<p>The reason is because they&rsquo;re dirty black magic.</p>

<p>Hopefully, I can help get you over the hump and onto a working implementation. Let&rsquo;s
take a problem I had last week:</p>

<h2>Do this if that, and then be done</h2>

<p>I was working with a group who wanted to set a list of domains that would
bypass their web proxy for a specific network interface on an OS X workstation.
It sounds so simple, because it was. Due to the amount of time I had on-site,
I wrote a class with some nasty <code>exec</code> statements, a couple of facts, and some
conditional logic because that&rsquo;s what you do when you&rsquo;re in a hurry&hellip;but it
doesn&rsquo;t make it right. When I left, I hacked up a type and provider, and it&rsquo;s
a GREAT example because you probably have a similar problem.  Let&rsquo;s look at the
information we have:</p>

<p>The list of network interfaces:</p>

<figure class='code'><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='bash'><span class='line'>└▷ networksetup -listallnetworkservices
</span><span class='line'>An asterisk <span class="o">(</span>*<span class="o">)</span> denotes that a network service is disabled.
</span><span class='line'>Bluetooth DUN
</span><span class='line'>Display Ethernet
</span><span class='line'>Ethernet
</span><span class='line'>FireWire
</span><span class='line'>Wi-Fi
</span><span class='line'>iPhone USB
</span><span class='line'>Bluetooth PAN
</span></code></pre></td></tr></table></div></figure>


<p>Getting the list of bypass domains for an interface:</p>

<figure class='code'><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='bash'><span class='line'>└▷ networksetup -getproxybypassdomains Ethernet
</span><span class='line'>www.garylarizza.com
</span><span class='line'>*.corp.net
</span><span class='line'>10.13.1.3/24
</span></code></pre></td></tr></table></div></figure>


<p>The message displayed when no domains are set for an interface:</p>

<figure class='code'><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'>└▷ networksetup -getproxybypassdomains FireWire
</span><span class='line'>There aren<span class="err">&#39;</span>t any bypass domains <span class="nb">set </span>on FireWire.
</span></code></pre></td></tr></table></div></figure>


<p>Setting the list of bypass domains for an interface:</p>

<figure class='code'><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'>└▷ networksetup -setproxybypassdomains Ethernet <span class="s1">&#39;*.corp.net&#39;</span> <span class="s1">&#39;10.13.1.3/24&#39;</span> <span class="s1">&#39;www.garylarizza.com&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Perfect &ndash; all of that is done with a single binary, and it&rsquo;s pretty straightforward. Let&rsquo;s look at the type I ended up creating for this problem:</p>

<figure class='code'><figcaption><span>lib/puppet/type/mac_proxy_bypassdomains.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">newtype</span><span class="p">(</span><span class="ss">:mac_proxy_bypassdomains</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">desc</span> <span class="s2">&quot;Puppet type that models bypass domains for a network interface on OS X&quot;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">ensurable</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:namevar</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Interface name - currently must be &#39;friendly&#39; name (e.g. Ethernet)&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:domains</span><span class="p">,</span> <span class="ss">:array_matching</span> <span class="o">=&gt;</span> <span class="ss">:all</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Domains which should bypass the proxy&quot;</span>
</span><span class='line'>    <span class="k">def</span> <span class="nf">insync?</span><span class="p">(</span><span class="n">is</span><span class="p">)</span>
</span><span class='line'>      <span class="n">is</span><span class="o">.</span><span class="n">sort</span> <span class="o">==</span> <span class="n">should</span><span class="o">.</span><span class="n">sort</span>
</span><span class='line'>    <span class="k">end</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>The type uses a namevar parameter called &lsquo;name&rsquo;, which is the name of the network interface.
This means that we can set one list of bypass domains for every network interface.
There&rsquo;s a single property, &lsquo;domains&rsquo; that accepts an array of domains that should bypass
the proxy for the network interface. I&rsquo;ve overridden the <code>insync?</code> method for the domains
property to sort the array values on both ends &ndash; this means that the ORDER of the domains
doesn&rsquo;t matter, I only care that the domains specified exist on the system. Finally, the
type is ensurable (which means that we can create a list of domains and remove/destroy
the list of domains for a network interface).</p>

<h2>Setup the provider</h2>

<p>Okay, so we&rsquo;ve defined the problem, seen how to interact with the system to get us the
data that we need, setup a type to model the data, and now the last thing left to do
is to wire up the provider to make the binary calls we need and return the data we
want.</p>

<h3>Typos are not your friend.</h3>

<p>The first thing you will encounter is &ldquo;Puppet&rsquo;s predictable naming pattern&rdquo;
that is used by the Puppet autoloader. Typos are not fun, and omitting a single
letter in either the filename or the provider name will render your provider
(emotionally) unavailable to Puppet. Our type is called &lsquo;mac_proxy_bypassdomains&rsquo;,
as types are generally named along the lines of &lsquo;what does this data model?&rsquo; The
provider name is generally the name of the underlying technology that&rsquo;s doing
the modeling. For the package type, the providers are named after the package
management systems (e.g. yum, apt, pacman, zypper, pip), for the file type, the
providers are loosely named for the operatingsystem kernel type on which files
are to be created (e.g. windows, posix). In our example, I simply chose to name
the provider &lsquo;ruby&rsquo; because, as a Puppet Labs employee, <strong>I TOO</strong> suck at naming
things.</p>

<p>Here&rsquo;s a tree of my module to understand how the type and provider files are
to be laid out:</p>

<figure class='code'><figcaption><span>Module tree </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='bash'><span class='line'>├── Modulefile
</span><span class='line'>├── README.markdown
</span><span class='line'>└── lib
</span><span class='line'>    └── puppet
</span><span class='line'>        ├── provider
</span><span class='line'>        │   ├── mac_proxy_bypassdomains
</span><span class='line'>        │   │   └── ruby.rb
</span><span class='line'>        └── <span class="nb">type</span>
</span><span class='line'>            └── mac_proxy_bypassdomains.rb
</span></code></pre></td></tr></table></div></figure>


<p>As you can see from above, the name of both the type and provider must <strong>EXACTLY</strong>
match the filename of their corresponding files. Also, the provider file lives
in a directory named after the type. There are MANY things that can be typoed here
(filenames, foldernames, type/provider names in their files), so be absolutely sure
that you&rsquo;ve named your files correctly.</p>

<p>The reason for all this naming bullshit is because of the way Puppet syncs down
plugin files (coincidentally, with a <a href="http://docs.puppetlabs.com/guides/plugins_in_modules.html">process known as Pluginsync)</a>.
Everything in the <code>lib</code> directory in a Puppet module is going to get synced down
to your nodes inside <a href="http://docs.puppetlabs.com/references/latest/configuration.html#vardir">the <code>vardir</code> directory</a> on
the node itself. The <code>vardir</code> is a known library path to Puppet, and all files
in the <code>vardir</code> are treated as if they had lived in Puppet&rsquo;s source code (in
the same relative paths).  Because the Puppet source code has all type files in
the <code>lib/puppet/type</code> directory, all <strong>CUSTOM</strong> types must go in the module&rsquo;s
<code>lib/puppet/type</code> directory for confirmity.  This is repeated for <strong>EVERY</strong>
custom Puppet/Facter plugin (including custom facts, custom functions, and
etc&hellip;).</p>

<h3>More scaffolding</h3>

<p>Let&rsquo;s layout the shell of our provider, first, to ensure that we haven&rsquo;t typoed
anything. Here&rsquo;s the provider declaration:</p>

<figure class='code'><figcaption><span>lib/puppet/type/mac_proxy_bypassdomains/ruby.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">type</span><span class="p">(</span><span class="ss">:mac_proxy_bypassdomains</span><span class="p">)</span><span class="o">.</span><span class="n">provide</span><span class="p">(</span><span class="ss">:ruby</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="c1"># Provider work goes here</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that the name of the type and the name of the provider are symbolized (i.e.
they&rsquo;re prepended with a colon). Like I mentioned above, they must be spelled
EXACT or Puppet will complain very loudly. You may see variants on that
declaration line because there are multiple ways in Ruby to extend a class
object. The method I&rsquo;ve listed above is the &lsquo;generally accepted best-practice&rsquo;, which
is to say it&rsquo;s the way we&rsquo;re doing it this month.</p>

<p>Congrats! You have THE SHELL of a provider that has yet to do a single goddamn thing!
Technically, you&rsquo;re further than about 90% of other Puppet users at this point! Let&rsquo;s
go the additional 20% (since we&rsquo;re basing this on a mangement metric of 110%) by
wiring up the methods and making the damn thing work!</p>

<h3>Are you (en)sure about this?</h3>

<p>We&rsquo;ve explained before that a type is &lsquo;ensurable&rsquo; when you can check for its existance
on a system, create it when it doesn&rsquo;t exist (and it SHOULD exist), and destroy it when
it does exist (and it SHOULDN&rsquo;T exist). The bare minimum amount of methods necessary
to make a type ensurable is three, and they&rsquo;re called <code>exists?</code>, <code>create</code>, and <code>destroy</code>.</p>

<h3>Method: <code>exists?</code></h3>

<p>The <code>exists?</code> method is a predicate method &ndash; that means it should either return
the boolean <code>true</code> or <code>false</code> value based on whether the bypass domain list
exists. Puppet will always call the <code>exists?</code> provider method to determine if
that &lsquo;thing&rsquo; (in this case, &lsquo;thing&rsquo; means &lsquo;a list of domains to bypass for a
specific network interface&rsquo;) exists before calling any other methods. How do we
know if this thing exists? Like I showed before, you need to run the
<code>networksetup -getproxybypassdomains</code> command and pass the interface name.  If
it returns &lsquo;There aren&rsquo;t any bypass domains set on (interface name)&rsquo;, then the
list doesn&rsquo;t exist. Let&rsquo;s do some binary execution&hellip;</p>

<h4>Calling binaries from Puppet</h4>

<p>Puppet provides some helper syntax around basic actions that most providers
perform. MOST providers are going to need to call out to an external binary
(e.g. yum, apt, etc&hellip;) at some point, and so Puppet allows you to create
your own method JUST for a system binary. The <code>commands</code> method abstracts
all the dirtyness of making a method for each system binary you want to call.
The way you use the <code>commands</code> method is like so:</p>

<figure class='code'><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='ruby'><span class='line'><span class="n">commands</span> <span class="ss">:networksetup</span> <span class="o">=&gt;</span> <span class="s1">&#39;networksetup&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>The <code>commands</code> method accepts a hash whose key must be a symbolized name. The
CONVENTION is to use a symbolized name that matches the binary name, but it&rsquo;s
not REQUIRED to do so. The value for that symbolized key MUST be the binary
name. Note that I&rsquo;ve not passed a full path to the binary. Why? Well, Puppet
will automatically do a path lookup for that binary and store its full path
for use when the binary is invoked. We don&rsquo;t REQUIRE you to pass the full
path because sometimes the same binary exists in different locations for
different operatingsystems. Instead of creating a provider for each OS you
manage with Puppet, we abstract away the path stuff. You <strong>CAN</strong> still
pass a full path as a value, but if you elect to do that an the binary doesn&rsquo;t
exist at that path, Puppet will disqualify the provider and you&rsquo;ll be quite
upset.</p>

<p>In the event that Puppet <strong>CANNOT</strong> find this binary, it will disqualify the
entire provider, and you&rsquo;ll get a message saying as much in the debug output
of your Puppet run. Because of that, the <code>commands</code> method is a good way to
confine your provider to a specific system or class of system.</p>

<p>When the <code>commands</code> method is successfully invoked, you will get a new provider
method named after the <strong>SYMBOLIZED</strong> key, and not necessarily the binary
name (unless you made them the same). After the above command is evaluated,
Puppet will now have a <code>networksetup()</code> method in our provider. The argument
to the <code>networksetup</code> method should be an array of arguments that are passed
to the binary. It&rsquo;s c-style, so each element is going to be individually
quoted. You can run into issues here if you pass values containing quotes
as part of your argument array. Read that again &ndash; quoting your values is
totally acceptable (e.g. [&lsquo;foo&rsquo;, &lsquo;bar&rsquo;]), but passing a value that contains
quotes can potentially cause problems (e.g. [&ldquo;&lsquo;foo&rsquo;&rdquo;, &ldquo;&lsquo;bar&rsquo;&rdquo;]).</p>

<p>You&rsquo;re probably thinking &ldquo;Why the hell would I go through this trouble when
I can use the <code>%x{}</code> syntax in ruby to execute a shell command?!&rdquo; And to that
I would say &ldquo;Quit yelling at me&rdquo; and also &ldquo;Because: testing.&rdquo; When you write
spec tests for your provider (which will be covered in a later blog post, since
it&rsquo;s its OWN path of WTF), you&rsquo;re going to need to mock out calls to the system
during your tests (i.e. sometimes you may be running the tests on a system that
doesn&rsquo;t have the binary you&rsquo;re meant to be calling in your provider. You don&rsquo;t
want the tests to fail due to the absence of a binary file). The <code>%x{}</code>
construct in Ruby is hard to mock out, but a method of our provider is a relatively
easy thing to mock out. Also &ndash; see the path problem above. We don&rsquo;t STOP you
from doing <code>%x{}</code> in your code (it will still totally work), but we give you
a couple of good reasons to NOT do it.</p>

<h4>Objects are a provider&rsquo;s best friend</h4>

<p>Within your provider, you&rsquo;re going to be doing lots of system calls and data
manipulation. Often we&rsquo;re asked whether you do that ugliness inside the main
methods (i.e. inside the <code>exists?</code> method directly), or if you create a
helper method for some of this data manipulation. The answer I usually give
is that you should probably create a helper method if:</p>

<ul>
<li>The code is going to be called more than once</li>
<li>The code does something that would be tricky to test (like reading from a file)</li>
<li>Complexity would be reduced by creating a helper method</li>
</ul>


<p>The act of getting a list of domains for a specific interface is definitely
going to be utilized in more than one place in our provider (we&rsquo;ll use it
in the <code>exists?</code> method as well as in a &lsquo;getter&rsquo; method for the <code>domains</code>
property). Also, you could argue that it might be tricky to test since it&rsquo;s
going to be a binary call that&rsquo;s going to return some data. Because of this,
let&rsquo;s create a helper method that returns a list of domains for a specific
interface:</p>

<figure class='code'><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='ruby'><span class='line'><span class="k">def</span> <span class="nf">get_proxy_bypass_domains</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>  <span class="k">begin</span>
</span><span class='line'>    <span class="n">output</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-getproxybypassdomains&#39;</span><span class="p">,</span> <span class="n">int</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="k">rescue</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:ExecutionFailure</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span><span class='line'>    <span class="no">Puppet</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&quot;#get_proxy_bypass_domains had an error -&gt; </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="kp">nil</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>  <span class="n">domains</span> <span class="o">=</span> <span class="n">output</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">sort</span>
</span><span class='line'>  <span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="n">domains</span><span class="o">.</span><span class="n">first</span> <span class="o">=~</span> <span class="sr">/There aren\&#39;t any bypass domains set/</span>
</span><span class='line'>  <span class="n">domains</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Ruby convention is to use underscores (i.e. versus camelCase or hyphens) in
method names. You want to give your methods very descriptive names based on
what it is that they DO. In this case, <code>get_proxy_bypass_domains</code> seems
adequately descriptive. Also, you should err on the side of readability when
you&rsquo;re writing code. You can get pretty creative with Ruby metaprogramming,
but that can quickly become hard to follow (and then you&rsquo;re just a dick).
Finally, error-handling is a good thing. If you&rsquo;re going to do any error-handling,
though, be very specific about the errors you catch/rescue. When you have a rescue
block, make sure you catch a specific exception class (in the case above, we&rsquo;re
catching a Puppet::ExecutionFailure &ndash; which means the binary is returning a
non-zero exit code).</p>

<p>The code above will return an array containing all the domains, or it will
return <code>nil</code> if domains aren&rsquo;t found or the <code>networksetup</code> binary had an issue.</p>

<p>Using the helper method above, here&rsquo;s what the final <code>exists?</code> method looks like:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">exists?</span>
</span><span class='line'>  <span class="n">get_proxy_bypass_domains</span><span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">)</span> <span class="o">!=</span> <span class="kp">nil</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>All provider methods have the ability to access the &lsquo;should&rsquo; values for the resource
(and by that I mean the values that are set in the Puppet maniest on the Puppet master
server, or locally if you&rsquo;re using <code>puppet apply</code>). Those values reside in the <code>resource</code>
method that responds with a hash. In the code above, <code>resource[:name]</code> will return the
network interface name (e.g. Ethernet, FireWire, etc&hellip;) that was specified in the
Puppet manifest. The exists method will return true of a list of domains exists for
an interface, or it will return false if a list of domains does not exist (i.e.
<code>get_proxy_bypass_domains</code> returns <code>nil</code>).</p>

<h3>Method: <code>create</code></h3>

<p>The <code>create</code> method is called when <code>exists?</code> returns false and a resource has an
<code>ensure</code> value set to <code>present</code>. Because of this, you don&rsquo;t need to call the <code>exists?</code>
method explicitly in <code>create</code> &ndash; it&rsquo;s already been evaluated. Remember from above that
the <code>-setproxybypassdomains</code> argument to the <code>networksetup</code> binary will set a domain
list, so the <code>create</code> method is going to be very short-and-sweet:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">create</span>
</span><span class='line'>  <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setproxybypassdomains&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:domains</span><span class="o">]]</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>In the end, the <code>create</code> method will call the <code>networksetup</code> binary with the <code>-setproxybypassdomains</code>
argument, pass the interface name (from <code>resource[:name]</code>) and pass an array of domain values (which
comes from <code>resource[:domains]</code>). That&rsquo;s it; it&rsquo;s done!</p>

<h3>Method: <code>destroy</code></h3>

<p>The <code>destroy</code> method is easier than the <code>create</code> method:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">destroy</span>
</span><span class='line'>  <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setproxybypassdomains&#39;</span><span class="p">,</span> <span class="kp">nil</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Here, we&rsquo;re calling <code>networksetup</code> with the <code>-setproxybypassdomains</code> argument
and passing nothing else. This will initialize the list and set it to be empty.</p>

<h2>Synchronizing properties</h2>

<h3>Getter method: <code>domains</code></h3>

<p>At this point our type is ensurable, which means we can create and destroy resources.
What we CAN&rsquo;T do, however, is change the value of any properties that are out-of-sync. A
property is out-of-sync when the value discovered by Puppet on the node differs from the
value in the catalog (i.e. set by the Puppet manifest using the DSL on the Puppet master).
Just like <code>exists?</code> is called to determine if a resource exists, Puppet needs a way to
get the current value for a property on a node. The method that gets this value is
called the &lsquo;getter method&rsquo; for a property, and its name must match the name of the
property. Because we have a property called <code>domains</code>, the provider must have a <code>domains</code>
method that returns a value (in this case, an array of domains to be bypassed by the
proxy). We&rsquo;ve already written a helper method that does this work for us, so the
<code>domains</code> getter method is pretty easy:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">domains</span>
</span><span class='line'>  <span class="n">get_proxy_bypass_domains</span><span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Tada! Just call the helper method and pass the interface name. Boom &ndash; instant array
of values. The getter method will return the &lsquo;is&rsquo; value, because that&rsquo;s what the value
<strong>IS</strong> (currently on the node). Get it? Anyone? The <strong>IS</strong> value is the other side of
the coin to the &lsquo;should&rsquo; value (that comes from the Puppet manifest) because that&rsquo;s
what the value <strong>SHOULD</strong> be set on the node.</p>

<h3>Setter method: <code>domains=</code></h3>

<p>If the getter method (e.g. <code>domains</code>) returns a value that doesn&rsquo;t match the value
in the catalog, then Puppet changes the value on the node and sets it to the value
in the catalog. It does this by calling the &lsquo;setter&rsquo; method for the property, which
is the name of the property and the equals ( = ) sign. In this case, the setter
method for the <code>domains</code> property must be called <code>domains=</code>.  It looks like this:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">domains</span><span class="o">=</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span><span class='line'>  <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setproxybypassdomains&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="n">value</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Setter methods are always passed a single argument &ndash; the &lsquo;should&rsquo; value of the property.
In our example, we&rsquo;re calling the <code>networksetup</code> binary with the <code>-setproxybypassdomains</code>
argument, passing the name of the interface, and then passing the &lsquo;should&rsquo; value &ndash; or
the array of domains. It&rsquo;s easy, it&rsquo;s one line, and I love it when a plan comes together</p>

<h2>Putting the whole damn thing together</h2>

<p>I&rsquo;ve broken down the provider line by line, but here&rsquo;s the entire file:</p>

<figure class='code'><figcaption><span>lib/puppet/provider/mac_proxy_bypassdomains/ruby.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>
<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">type</span><span class="p">(</span><span class="ss">:mac_proxy_bypassdomains</span><span class="p">)</span><span class="o">.</span><span class="n">provide</span><span class="p">(</span><span class="ss">:ruby</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">commands</span> <span class="ss">:networksetup</span> <span class="o">=&gt;</span> <span class="s1">&#39;networksetup&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">get_proxy_bypass_domains</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
</span><span class='line'>    <span class="k">begin</span>
</span><span class='line'>      <span class="n">output</span> <span class="o">=</span> <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-getproxybypassdomains&#39;</span><span class="p">,</span> <span class="n">int</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>    <span class="k">rescue</span> <span class="ss">Puppet</span><span class="p">:</span><span class="ss">:ExecutionFailure</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span><span class='line'>      <span class="no">Puppet</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&quot;#get_proxy_bypass_domains had an error -&gt; </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'>      <span class="k">return</span> <span class="kp">nil</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>    <span class="n">domains</span> <span class="o">=</span> <span class="n">output</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">sort</span>
</span><span class='line'>    <span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="n">domains</span><span class="o">.</span><span class="n">first</span> <span class="o">=~</span> <span class="sr">/There aren\&#39;t any bypass domains set/</span>
</span><span class='line'>    <span class="n">domains</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="nf">exists?</span>
</span><span class='line'>    <span class="n">get_proxy_bypass_domains</span><span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">)</span> <span class="o">!=</span> <span class="kp">nil</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="nf">destroy</span>
</span><span class='line'>    <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setproxybypassdomains&#39;</span><span class="p">,</span> <span class="kp">nil</span><span class="o">]</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="nf">create</span>
</span><span class='line'>    <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setproxybypassdomains&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:domains</span><span class="o">]]</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="nf">domains</span>
</span><span class='line'>    <span class="n">get_proxy_bypass_domains</span><span class="p">(</span><span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</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="nf">domains</span><span class="o">=</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span><span class='line'>    <span class="n">networksetup</span><span class="p">(</span><span class="o">[</span><span class="s1">&#39;-setproxybypassdomains&#39;</span><span class="p">,</span> <span class="n">resource</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="n">value</span><span class="o">]</span><span class="p">)</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>


<h2>Testing the type/provider</h2>

<p>And that&rsquo;s it, we&rsquo;re done!  The last thing to do is to test it out.  You can
test out your provider in one of two ways: the first is to add the module to
the modulepath of your Puppet master and include it that way, or test it
locally by setting the <code>$RUBYLIB</code> environmental variable to point to the <code>lib</code>
directory of your module (which is the more preferred method since it won&rsquo;t
serve it out to all of your nodes without it being tested). Because this module
is on my system at <code>/users/glarizza/src/puppet-mac_proxy</code>, here&rsquo;s how my
<code>$RUBYLIB</code> is set:</p>

<figure class='code'><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'><span class="nb">export </span><span class="nv">RUBYLIB</span><span class="o">=</span>/users/glarizza/src/puppet-mac_proxy/lib
</span></code></pre></td></tr></table></div></figure>


<p>Next, we need to create a resource declaration to try and set a couple of
bypass domains. I&rsquo;ll create a <code>tests</code> directory and simple test file in
<code>tests/mac_proxy_bypassdomains.pp</code>:</p>

<figure class='code'><figcaption><span>tests/mac_proxy_bypassdomains.pp </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='puppet'><span class='line'><span class="nc">mac_proxy_bypassdomains</span> <span class="p">{</span> <span class="s1">&#39;Ethernet&#39;</span><span class="p">:</span>
</span><span class='line'>  <span class="nt">ensure</span>  <span class="p">=&gt;</span> <span class="s1">&#39;present&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">domains</span> <span class="p">=&gt;</span> <span class="p">[</span><span class="s1">&#39;www.garylarizza.com&#39;</span><span class="p">,</span><span class="s1">&#39;*.puppetlabs.com&#39;</span><span class="p">,</span><span class="s1">&#39;10.13.1.3/24&#39;</span><span class="p">],</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Finally, let&rsquo;s run Puppet and test it out:</p>

<figure class='code'><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=''><span class='line'>└▷ puppet apply ~/src/puppet-mac_proxy/tests/mac_proxy_bypassdomains.pp
</span><span class='line'>Notice: Compiled catalog for satori.local in environment production in 0.06 seconds
</span><span class='line'>Notice: /Stage[main]//Mac_proxy_bypassdomains[Ethernet]/domains: domains changed [] to 'www.garylarizza.com *.puppetlabs.com 10.13.1.3/24'
</span><span class='line'>Notice: Finished catalog run in 3.47 seconds</span></code></pre></td></tr></table></div></figure>


<p><strong>NOTE:</strong> If you run this as a local user, you will be prompted by OS X to
enter an administrative password for a change.  Since Puppet will ultimately be
run as root on OS X when we&rsquo;re NOT testing out code, this shouldn&rsquo;t be required
during a normal Puppet run. To test this out (i.e. that you don&rsquo;t always have to
enter an admin password in a pop-up window), you&rsquo;ll need to <code>sudo -s</code> to
change to root, set the <code>$RUBYLIB</code> as the root user, and then run Puppet again.</p>

<p>And that&rsquo;s it &ndash; looks like our code worked! To check and make sure it will notice a
change, open System Preferences, then the Network pane, click on the Ethernet
interface, then the Advanced button, then the Proxies tab, and finally note the
&lsquo;Bypass proxy settings&hellip;&rsquo; text box at the bottom of the screen (now do you see
why we automate this shit?!). Make a change to the entries in there and run
Puppet again &ndash; it should correct it for you</p>

<h2>Wait&hellip;so that was it?  Really?  We&rsquo;re done?</h2>

<p>Yeah, that was a whole type and provider. Granted, it has only one property and it&rsquo;s
not too complicated, but that&rsquo;s the point. We&rsquo;ve still got some latent bugs (the
network interface passed must be capitalized exactly like OS X expects it, we could
do some better error handling, etc&hellip;), and the type doesn&rsquo;t work with <code>puppet resource</code>
(yet), but we&rsquo;ll handle all of these things in the next blog post (or two&hellip;or three).</p>

<p>Until then, take this time to crack open a type and a provider for something that&rsquo;s
been pissing you off and FIX it!  Better yet, push it up to Github, tweet about it,
and post it up <a href="http://forge.puppetlabs.com">on The Forge</a> so the rest of the
community can use it!</p>

<p>Like always, feel free to comment, tweet me (@glarizza), email me (gary <strong>AT</strong> puppetlabs <strong>DOT</strong> com),
or use the social media platform of choice to get a hold of me (Snapchats may or may not
get a response. Maybe.)  Cheers!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Fun With Puppet Providers - Part 1 of Whatever]]></title>
    <link href="http://garylarizza.com/blog/2013/11/25/fun-with-providers/"/>
    <updated>2013-11-25T20:47:00-06:00</updated>
    <id>http://garylarizza.com/blog/2013/11/25/fun-with-providers</id>
    <content type="html"><![CDATA[<p>I don&rsquo;t know why I write blog posts &ndash; everybody in open-source software knows
that the code <em>IS</em> the documentation. If you&rsquo;ve ever tried to write a Puppet
type/provider, you know this fact better than ANYONE. To this day, when someone
asks me for the definitive source on this activity I usually refer them first
to <a href="http://www.amazon.com/Puppet-Types-Providers-Dan-Bode/dp/1449339328">Nan Liu and Dan Bode&rsquo;s awesome Types and Providers
book</a>
(which REALLY is a fair bit of quality information), and THEN to the source
code for Puppet. Everything else falls in-between those sources (sadly).</p>

<p>As someone who truly came from knowing absolute fuckall about Ruby and only
marginally more than that about Puppet, I&rsquo;ve walked through the valley of the
shadow of self.instances and have survived to tell the tale. That&rsquo;s what this
post is about &ndash; hopefully some GOOD information if you want to start writing
your own Puppet type and provider. I also wrote this because this knowledge
has been passed down from Puppet employee to Puppet employee, and I wanted
to break the priesthood being held on type and provider magic. If you don&rsquo;t
hear from me after tomorrow, well, then you know what happened&hellip;</p>

<h2>Because 20 execs in a defined type&hellip;</h2>

<p>What would drive someone to write a custom type and provider for Puppet anyhow?
Afterall, you can do ANYTHING IMAGINABLE in the Puppet DSL*! After drawing
back my sarcasm a bit, let me explain where the Puppet DSL tends to fall over
and the idea of a custom type and provider starts becoming more than just an
incredibly vivid dream:</p>

<ul>
<li>You have more than a couple of exec statements in a single class/defined type that have multiple conditional
properties like &lsquo;onlyif&rsquo; and/or &lsquo;unless&rsquo;.</li>
<li>You need to use pure Ruby to manipulate data and parse it through a system binary</li>
<li>Your defined type has more conditional logic than your pre-nuptual agreement</li>
<li>Any combination of similar arguments related to the above</li>
</ul>


<p>If the above sounds familiar to you, then you&rsquo;re probably ready to build your own custom Puppet type and provider.
Do note that custom types and providers are written in Ruby and not the Puppet DSL. This can initially feel
very scary, but get over it (there are much scarier things coming).</p>

<p><em>* Just because you can doesn&rsquo;t mean you don&rsquo;t, in fact, suck.</em></p>

<h2>I&rsquo;m not your Type</h2>

<p>This blog post is going to focus on types and type-interaction, while later
posts will focus on providers and ultimately dirty provider tricks to win
friends and influence others.  Type and provider interaction can be totally
daunting for newcomers, let ALONE just naming files correctly due to Puppet&rsquo;s
predictable (note: anytime I write the word &ldquo;predictable&rdquo;, just substitute the
phrase &ldquo;annoying pain in the ass&rdquo;) naming pattern.  Let&rsquo;s break it down a bit
for you &ndash; somebody que Dre&hellip;</p>

<p>(NOTE: I&rsquo;m going to ASSUME you understand the fundamentals of a Puppet run already. If you&rsquo;re pretty hazy
on that concept, checkout <a href="http://docs.puppetlabs.com">docs.puppetlabs.com</a> for more information)</p>

<h2>Types are concerned about your looks</h2>

<p>The type file defines all the <em>properties</em> and <em>parameters</em> that can be used by your new custom resource.
Think of the type file like the opening stanza to a new Puppet class &ndash; we&rsquo;re describing all the tweakable
knobs and buttons to the new thing we&rsquo;re creating. The type file also gives you some added validation
abilities, which is very handy.</p>

<p>It&rsquo;s important to understand that there is a BIG difference between a &lsquo;property&rsquo; and a &lsquo;parameter&rsquo; with regard
to a type (even though they&rsquo;re both assigned values identically in a resource declaration).  Think of it this
way: a property is something that can be inspected and changed by Puppet, while a parameter is just helper data
that Puppet uses to do its job.  A property would be something like a file&rsquo;s mode.  You can inspect a file and
determine its mode, and you can even CHANGE a file&rsquo;s mode on disk. The file resource type also has a parameter
called &lsquo;backup&rsquo;.  Its sole job is to tell Puppet whether to backup the file to the filebucket before making
changes. This data is useful for Puppet during a run, but you can&rsquo;t inspect a file on disk and know definitively
whether Puppet is going to back it up or not (and it goes without saying that if you can&rsquo;t determine this aspect
about a file on disk just by inspecting it, than you also can&rsquo;t CHANGE this aspect about a file on disk either).
You&rsquo;ll see later where the property/parameter distinction becomes very important.</p>

<p>Recently I built a type modeling the setting of proxy data for network interfaces on OS X, so we&rsquo;ll use that as
a demonstration of a type.  It looks like the following:</p>

<figure class='code'><figcaption><span>lib/puppet/type/mac_web_proxy.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>
<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="ss">Puppet</span><span class="p">:</span><span class="ss">:Type</span><span class="o">.</span><span class="n">newtype</span><span class="p">(</span><span class="ss">:mac_web_proxy</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">desc</span> <span class="s2">&quot;Puppet type that models a network interface on OS X&quot;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">ensurable</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:namevar</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Interface name - currently must be &#39;friendly&#39; name (e.g. Ethernet)&quot;</span>
</span><span class='line'>    <span class="n">munge</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
</span><span class='line'>      <span class="n">value</span><span class="o">.</span><span class="n">downcase</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>    <span class="k">def</span> <span class="nf">insync?</span><span class="p">(</span><span class="n">is</span><span class="p">)</span>
</span><span class='line'>      <span class="n">is</span><span class="o">.</span><span class="n">downcase</span> <span class="o">==</span> <span class="n">should</span><span class="o">.</span><span class="n">downcase</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="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_server</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:authenticated_username</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Username for proxy authentication&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newparam</span><span class="p">(</span><span class="ss">:authenticated_password</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Password for proxy authentication&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_authenticated</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>    <span class="n">newvalues</span><span class="p">(</span><span class="ss">:true</span><span class="p">,</span> <span class="ss">:false</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_port</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>    <span class="n">newvalues</span><span class="p">(</span><span class="sr">/^\d+$/</span><span class="p">)</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>First note the type file&rsquo;s path in the grey titlebar of the graphic: <code>lib/puppet/type/mac_web_proxy.rb</code>
This path is relative to the module that you&rsquo;re building, and it&rsquo;s VERY important that it be named
EXACTLY this way to appease Puppet&rsquo;s predictable naming pattern.  The name of the file directly correllates
to the name of the type listed in the <code>Puppet::Type.newtype()</code> method.</p>

<p>Next, let&rsquo;s look at a sample parameter declaration &ndash; for starters, let&rsquo;s look at the &lsquo;authenticated_password&rsquo;
parameter declaration on line 24 of the above type.  The <code>newparam()</code> method is called and the lone argument
passed is the symbolized name of our parameter (i.e. it&rsquo;s prepended with a colon).  This parameter
provides the password to use when setting up an authenticated web proxy on OS X.
It&rsquo;s a parameter because as far as I know, there&rsquo;s no way for me to query the system for this password
(it&rsquo;s obfuscated in the GUI and I&rsquo;m not entirely certain where it&rsquo;s stored on-disk).
If there were a way for us to query this value from the system, then we could turn it
into a property (since we could both &lsquo;GET&rsquo; as well as &lsquo;SET&rsquo; the value). As of right
now, it exists as helper data for when I need to setup an authenticated proxy.</p>

<p>Having seen a parameter, let&rsquo;s look at the &lsquo;proxy_server&rsquo; property that&rsquo;s declared on
line 16 of the type file above. We&rsquo;re able to both query the system for this value,
as well as change/set the value by using the <code>networksetup</code> binary, so it&rsquo;s able to
be &lsquo;synchronized&rsquo; (according to Puppet). Because of this, it must be a property.</p>

<h2>Just enough validation</h2>

<p>The second major function of the type file is to provide methods to validate property
and parameter data that is being passed. There are two methods to validate this data,
and one method that allows you to massage the data into an acceptable format (which
is called &lsquo;munging&rsquo;).</p>

<h3>validate()</h3>

<p>The first method, named &lsquo;validate&rsquo;, is widely believed to be the only successfully-named
method in the entire Puppet codebase. Validate accepts a block and allows you to perform
free-form validation in any way you prefer.  For example:</p>

<figure class='code'><figcaption><span>lib/puppet/type/user.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">validate</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
</span><span class='line'>  <span class="k">raise</span> <span class="no">ArgumentError</span><span class="p">,</span> <span class="s2">&quot;Passwords cannot include &#39;:&#39;&quot;</span> <span class="k">if</span> <span class="n">value</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span> <span class="ow">and</span> <span class="n">value</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="s2">&quot;:&quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>This example, pulled straight from the Puppet codebase, will raise an error if a
password contains a colon. In this case, we&rsquo;re looking for a specific exception
and are raising errors accordingly.</p>

<h3>newvalues()</h3>

<p>The second method, named &lsquo;newvalues&rsquo;, accepts a regex that property/parameter values
need to match (if you&rsquo;re one of the 8 people in the world that speak regex fluently),
or a list of acceptable values. From the example above:</p>

<figure class='code'><figcaption><span>lib/puppet/type/mac_web_proxy.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_authenticated</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>    <span class="n">newvalues</span><span class="p">(</span><span class="ss">:true</span><span class="p">,</span> <span class="ss">:false</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newproperty</span><span class="p">(</span><span class="ss">:proxy_port</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Proxy Server setting for the interface&quot;</span>
</span><span class='line'>    <span class="n">newvalues</span><span class="p">(</span><span class="sr">/^\d+$/</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h3>munge()</h3>

<p>The final method, named &lsquo;munge&rsquo; accepts a block like <code>newvalues</code> but allows you to
convert an unacceptable value into an acceptable value. Again, this is from the example above:</p>

<figure class='code'><figcaption><span>lib/puppet/type/mac_web_proxy.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">munge</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
</span><span class='line'>  <span class="n">value</span><span class="o">.</span><span class="n">downcase</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>In this case, we want to ensure that the parameter value is lower case. It&rsquo;s not
necessary to throw an error, but rather it&rsquo;s acceptable to &lsquo;munge&rsquo; the value to
something that is more acceptable without alerting the user.</p>

<h2>Important type considerations</h2>

<p>You could write half a book just on how types work (and, again, check out the book
referenced above which DOES just that), but there are a couple of final considerations
that will prove helpful when developing your type.</p>

<h3>Defaulting values</h3>

<p>The <code>defaultto</code> method provides a default value should the user not provide one for
your property/parameter. It&rsquo;s a pretty simple construct, but it&rsquo;s important to
remember when you write spec tests for your type (which you ARE doing, right?) that
there will ALWAYS be values for properties/parameters that utilize <code>defaultto</code>. Here&rsquo;s a quick example:</p>

<figure class='code'><figcaption><span>Defaultto example </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='ruby'><span class='line'><span class="n">newparam</span><span class="p">(</span><span class="ss">:enable_lacp</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">defaultto</span> <span class="ss">:true</span>
</span><span class='line'>  <span class="n">newvalues</span><span class="p">(</span><span class="ss">:true</span><span class="p">,</span> <span class="ss">:false</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Ensurable types</h3>

<p>A resource is considered &lsquo;ensurable&rsquo; when its presence can be verified (i.e. it
exists on the system), it can be created when it doesn&rsquo;t exist and it SHOULD, and
it can be destroyed when it exists and it SHOULDN&rsquo;T. The simplest way to tell
Puppet that a resource type is ensurable is to call the <code>ensurable</code> method within
the body of the type (i.e. outside of any property/parameter declarations). Doing
this will automatically create an &lsquo;ensure&rsquo; property that accepts values of &lsquo;absent&rsquo;
and &lsquo;present&rsquo; that are automatically wired to the &lsquo;exists?&rsquo;, &lsquo;create&rsquo; and &lsquo;destroy&rsquo;
methods of the provider (something I&rsquo;ll write about in the next post). Optionally,
you can choose to pass a block to the <code>ensurable</code> method and define acceptable
property values as well as the methods of the provider that are to be called. That
would look something like this:</p>

<figure class='code'><figcaption><span>lib/puppet/type/package.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="n">ensurable</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">newvalue</span><span class="p">(</span><span class="ss">:present</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">provider</span><span class="o">.</span><span class="n">install</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newvalue</span><span class="p">(</span><span class="ss">:absent</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">provider</span><span class="o">.</span><span class="n">uninstall</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newvalue</span><span class="p">(</span><span class="ss">:purged</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">provider</span><span class="o">.</span><span class="n">purge</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">newvalue</span><span class="p">(</span><span class="ss">:held</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">provider</span><span class="o">.</span><span class="n">hold</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>This means that instead of calling the <code>create</code> method to create a new resource that
SHOULD exist (but doesn&rsquo;t), Puppet is going to call the <code>install</code> method. Conversely,
it will call the <code>uninstall</code> method to destroy a resource based on this type. The
ensure property will also accept values of &lsquo;purged&rsquo; and &lsquo;held&rsquo; which will be wired up
to the <code>purge</code> and <code>hold</code> methods respectively.</p>

<h3>Namevars are unique little snowflakes</h3>

<p>Puppet has a <a href="http://docs.puppetlabs.com/puppet/2.7/reference/lang_resources.html#namenamevar">concept known as the &lsquo;namevar&rsquo; for a resource.</a>
If you&rsquo;re hazy about the concept check out the documentation, but basically it&rsquo;s the parameter
that describes the form of uniqueness for a resource type on the system. For the package resource
type, the &lsquo;name&rsquo; parameter is the namevar because the way you tell one package from another is
its name. For the file resource, it&rsquo;s the &lsquo;path&rsquo; parameter, because you can differentiate unique
files from each other according to their path (and not necessarily their filename, since filenames
don&rsquo;t have to be unique on systems).</p>

<p>When designing a type, it&rsquo;s important to consider WHICH parameter will be the namevar (i.e. how
can you tell unique resources from one another). To make a parameter the namevar, you simply
set the <code>:namevar</code> attribute to <code>:true</code> like below:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">newparam</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:namevar</span> <span class="o">=&gt;</span> <span class="ss">:true</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="c1"># Type declaration attributes here...</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Handling array values</h3>

<p>Nearly every property/parameter value that is declared for a resource is &lsquo;stringified&rsquo;, or
cast to a string. Sometimes, however, it&rsquo;s necessary to accept an array of elements as the
value for a property/parameter. To do this, you have to explicitly tell Puppet that you&rsquo;ll
be passing an array by setting the <code>:array_matching</code> attribute to <code>:all</code> (if you don&rsquo;t set
this attribute, it defaults to <code>:first</code>, which means that if you pass an array as a value
for a property/parameter, Puppet will only accept the FIRST element in that array).</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">newproperty</span><span class="p">(</span><span class="ss">:domains</span><span class="p">,</span> <span class="ss">:array_matching</span> <span class="o">=&gt;</span> <span class="ss">:all</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="c1"># Type declaration attributes here... </span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>If you set <code>:array_matching</code> to <code>:all</code>, EVERY value passed for that parameter/property will
be cast to an array (which means if you pass a value of &lsquo;foo&rsquo;, you&rsquo;ll get an array with a
single element &ndash; the string of &lsquo;foo&rsquo;).</p>

<h3>Documenting your property/parameter</h3>

<p>It&rsquo;s a best-practice to document the purpose of your property or parameter declaration, and
this can be done by passing a string to the <code>desc</code> method within the body of the property/parameter
declaration.</p>

<figure class='code'><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='ruby'><span class='line'><span class="n">newproperty</span><span class="p">(</span><span class="ss">:domains</span><span class="p">,</span> <span class="ss">:array_matching</span> <span class="o">=&gt;</span> <span class="ss">:all</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">desc</span> <span class="s2">&quot;Domains which should bypass the proxy&quot;</span>
</span><span class='line'><span class="c1"># Type declaration attributes here...</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Synchronization tricks</h3>

<p>Puppet uses a method called <code>insync?</code> to determine whether a property value is synchronized (i.e.
if Puppet needs to change its value, or it&rsquo;s set appropriately). You usually have no need to change
the behavior of this method since most of the properties you create for a type will have string
values (and the <code>==</code> operator does a good job of checking string equality). For structured data
types like arrays and hashes, however, that can be a bit trickier. Arrays, for example, are
ordered construct &ndash; they have a definitive idea of what the first element and the last element
of the array are. Sometimes you WANT to ensure that values are in a very specific order, and
sometimes you don&rsquo;t necessarily care about the ORDER that values for a property are set &ndash; you
just want to make sure that all of them are set.</p>

<p>If the latter cases sounds like what you need, then you&rsquo;ll need to override the behavior of the
<code>insync?</code> method. Take a look at the below example:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">newproperty</span><span class="p">(</span><span class="ss">:domains</span><span class="p">,</span> <span class="ss">:array_matching</span> <span class="o">=&gt;</span> <span class="ss">:all</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">desc</span> <span class="s2">&quot;Domains which should bypass the proxy&quot;</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">insync?</span><span class="p">(</span><span class="n">is</span><span class="p">)</span>
</span><span class='line'>    <span class="n">is</span><span class="o">.</span><span class="n">sort</span> <span class="o">==</span> <span class="n">should</span><span class="o">.</span><span class="n">sort</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>In this case, I&rsquo;ve overridden the <code>insync?</code> method to first sort the &lsquo;is&rsquo; value (or, the value that
was discovered by Puppet on the target node) and compare it with the sorted &lsquo;should&rsquo; value (or,
the value that was specified in the Puppet manifest when the catalog was compiled by the Puppet
master). You can do WHATEVER you want in here as long as <code>insync?</code> returns either a true or a
false value.  If <code>insync?</code> returns true, then Puppet determines that everything is in sync and
no changes are necessary, whereas if it returns false then Puppet will trigger a change.</p>

<h2>And this was the EASY part!</h2>

<p>Wow this went longer than I expected&hellip; and types are usually the &lsquo;easier&rsquo; bit
since you&rsquo;re only describing the format to be used by the Puppet admin in
manifests. There are some hacky type tricks that I&rsquo;ve not yet covered (i.e.
features, &lsquo;inheritance&rsquo;, and other meta-bullshit), but those will be saved for
a final &lsquo;dirty tips and tricks&rsquo; post.  In the next section, I&rsquo;ll touch on
providers (which is where all interaction with the system takes place), so
stay tuned for more brain-dumping-goodness&hellip;</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[From the Archive: Using Crankd]]></title>
    <link href="http://garylarizza.com/blog/2013/07/10/from-the-archive-using-crankd/"/>
    <updated>2013-07-10T09:51:00-05:00</updated>
    <id>http://garylarizza.com/blog/2013/07/10/from-the-archive-using-crankd</id>
    <content type="html"><![CDATA[<p>Supporting laptops in a managed environment is tricky (and doubly so if you allow them to be taken off your corporate network).  While you can be reasonably assured that your desktops will remain on and connected during the workday, it&rsquo;s not uncommon for laptops to go to sleep, change wireless access points, and even change between an Ethernet or AirPort connection several times during the day.  It&rsquo;s important to have a tool that can &ldquo;tweak&rdquo; certain settings in response to these changes.</p>

<p>This is where crankd comes in.</p>

<p>Crankd is a cool utility that&rsquo;s part of the Pymacadmin (<a href="http://code.google.com/p/pymacadmin/">http://code.google.com/p/pymacadmin/</a>) suite of tools co-authored by Chris Adams and Nigel Kersten.  Specifically crankd is a Python daemon that lets you trigger shell scripts, or execute Python methods, based upon state changes in SystemConfiguration, NSWorkspace and FSEvents.</p>

<h2>Use Cases</h2>

<p>It&rsquo;s easier to see how crankd can help you with a couple of scenarios:</p>

<ol>
<li><p>Your laptops, like all of the other machines in your organization, are bound to your corporate LDAP servers.  When they&rsquo;re on network, they will query the LDAP servers for things like authentication information.  Unless your corporate LDAP directory is accessible outside your corporate network, your laptops may exhibit the &ldquo;spinning wheel of death&rdquo; when they attempt to contact a suddenly-unreachable LDAP directory at the neighborhood Starbucks.  A solution to this is to remove the LDAP servers from your Search (and Contacts) path whenever the laptop is taken off-network and add the LDAP servers when you come back on-network.</p></li>
<li><p>Perhaps you&rsquo;re using Puppet, Munki, Chef, StarDeploy, Filewave, Absolute Manage, Casper, or any other configuration management system that needs to contact a centralized server for configuration information. Usually these tools will have your machine contact their servers once an hour or so, but this can be a problem if the machine is constantly sleeping and waking.  Plus if you take your machine off-network, you don&rsquo;t want it trying to contact a server that might not be reachable from the outside world. It would be nice to have your laptop &ldquo;phone home&rdquo; when it establishes a network connection on your corporate network, and skip this step when the laptop is taken outside your organization.</p></li>
<li><p>OS X allows you to set a preferred order for your network connections, but it would be nice to disable the AirPort when your laptop establishes an Ethernet connection.</p></li>
<li><p>Finally, maybe you have the need to perform an action whenever your laptop sleeps (or wakes), changes a network connection, mounts a volume, or runs a specific Application (whether it&rsquo;s located in the Applications directory or anywhere else on your machine).</p></li>
</ol>


<p>All of these situations can be made trivial through the help of crankd.</p>

<h2>How do I get it working?</h2>

<p>Crankd is a daemon, so it&rsquo;s running in the background while you work.  It uses an XML plist file that tells it which scripts (or which Python methods) to execute in response to specific state changes (like a network connection going up or down or a volume being mounted).  Since it&rsquo;s a small Python library, the files aren&rsquo;t huge and the entire finished installation is around 100 Kb (or larger with your custom code/scripts).  Lets download crankd and experiment with its settings:</p>

<ol>
<li><p><strong>Download the Pymacadmin source.</strong>  You can do this through Google Code or Github &ndash; I&rsquo;ll demonstrate the Github method.  Navigate to <a href="http://github.com/acdha/pymacadmin,">http://github.com/acdha/pymacadmin,</a> click the Downloads button, and download either the .tar.gz or the .zip version of the source code.  Drag it to your desktop and then double-click on the file to expand it.  It should open a folder named &ldquo;acdha-pymacadmin-<combination of 7 numbers and letters>&rdquo;</p></li>
<li><p><strong>Install crankd</strong>  Upon opening the pymacadmin folder, you should see a series of folders, readme files, and an &ldquo;install-crankd.sh&rdquo; installation script.  Let&rsquo;s open Terminal.app and navigate to the pymacadmin folder that we expanded on our desktop (you can type &ldquo;cd&rdquo; into Terminal.app and then drag and drop the folder into the Terminal window.  Hit the Return button on your keyboard to change to the directory.).  The install-crankd.sh script is executable, so run it by typing &ldquo;sudo ./install-crankd.sh&rdquo; into the Terminal window and hitting Return.  Enter your password when it prompts you</p></li>
<li><p><strong>Setup a plist file for crankd</strong>  If you&rsquo;ve never worked with crankd before, it&rsquo;s best to let it setup a configuration plist for you.  If you don&rsquo;t specify a configuration plist with the &ldquo;&mdash;config&rdquo; argument, or you don&rsquo;t have a com.googlecode.pymacadmin.crankd.plist file in your /Users/<username>/Library/Preferences folder, crankd will automatically create a sample plist for you.  Let&rsquo;s do that by typing &ldquo;/usr/local/sbin/crankd.py&rdquo; into Terminal and hitting the Return button.  Take a look at the sample configuration plist file:</p></li>
</ol>


<p>  &lt;?xml version=&ldquo;1.0&rdquo; encoding=&ldquo;UTF-8&rdquo;?>
  &lt;!DOCTYPE plist PUBLIC &ldquo;&ndash;//Apple Computer//DTD PLIST 1.0//EN&rdquo; &ldquo;<a href="http://www.apple.com/DTDs/PropertyList-1.0.dtd">http://www.apple.com/DTDs/PropertyList-1.0.dtd</a>&rdquo;>
  <plist version="1.0">
  <dict></p>

<pre><code>&lt;key&gt;NSWorkspace&lt;/key&gt;
&lt;dict&gt;
  &lt;key&gt;NSWorkspaceDidMountNotification&lt;/key&gt;
  &lt;dict&gt;
    &lt;key&gt;command&lt;/key&gt;
    &lt;string&gt;/bin/echo "A new volume was mounted!"&lt;/string&gt;
  &lt;/dict&gt;
  &lt;key&gt;NSWorkspaceDidWakeNotification&lt;/key&gt;
  &lt;dict&gt;
    &lt;key&gt;command&lt;/key&gt;
    &lt;string&gt;/bin/echo "The system woke from sleep!"&lt;/string&gt;
  &lt;/dict&gt;
  &lt;key&gt;NSWorkspaceWillSleepNotification&lt;/key&gt;
  &lt;dict&gt;
    &lt;key&gt;command&lt;/key&gt;
    &lt;string&gt;/bin/echo "The system is about to go to sleep!"&lt;/string&gt;
  &lt;/dict&gt;
&lt;/dict&gt;
&lt;key&gt;SystemConfiguration&lt;/key&gt;
&lt;dict&gt;
  &lt;key&gt;State:/Network/Global/IPv4&lt;/key&gt;
  &lt;dict&gt;
    &lt;key&gt;command&lt;/key&gt;
    &lt;string&gt;/bin/echo "Global IPv4 config changed"&lt;/string&gt;
  &lt;/dict&gt;
&lt;/dict&gt;
</code></pre>

<p>  </dict>
  </plist></p>

<p>This XML file has two main keys &ndash; one for NSWorkspace events (such as mounted volumes and sleeping/waking your laptop), and one for SystemConfiguration events (such as network state changes).  Followed by a key for the specific event that we&rsquo;re monitoring, a key specifying whether we&rsquo;ll be executing a command or a Python method in response to this event, and a string (or an array of strings, as we&rsquo;ll see later) specifying the actual command that&rsquo;s to be executed.  For all of the events in the sample plist, we&rsquo;re going to be echoing a message to the console.</p>

<ol>
<li><strong>Start crankd</strong>  Once crankd has been installed and your configuration plist file is setup, you&rsquo;re ready to let crankd monitor for state changes. Let&rsquo;s start crankd with the sample plist that was created in the previous step by executing the following command in Terminal &ldquo;/usr/local/sbin/crankd.py &mdash;config=/Users/<username>/Library/Preferences/com.googlecode.pymacadmin.crankd.plist&rdquo;  Remember to substitute your username for <username> in that command (if you don&rsquo;t know your username, you can type &ldquo;whoami&rdquo; into Terminal and hit the Return button).  If everything was executed correctly, you should see the following lines displayed in Terminal:</li>
</ol>


<p>  Module directory /Users/<username>/Library/Application Support/crankd does not exist: Python handlers will need to use absolute pathnames
  INFO: Loading configuration from /Users/<username>/Library/Preferences/com.googlecode.pymacadmin.crankd.plist
  INFO: Listening for these NSWorkspace notifications: NSWorkspaceWillSleepNotification, NSWorkspaceDidWakeNotification, NSWorkspaceDidMountNotification
  INFO: Listening for these SystemConfiguration events: State:/Network/Global/IPv4</p>

<p>It might look like Terminal isn&rsquo;t doing anything, but in all actuality crankd is listening for changes.  You can make crankd come to life by either connecting to (or disconnecting from) an AirPort network, sleeping/waking your machine, or mounting a volume (by inserting a USB memory stick, for example).  Performing any of these actions will cause crankd to echo messages to your Terminal window.  Here&rsquo;s the message I received when I disconnected from an AirPort network:</p>

<p>  INFO: SystemConfiguration: State:/Network/Global/IPv4: executing /bin/echo &ldquo;Global IPv4 config changed&rdquo;
  Global IPv4 config changed</p>

<p>To quit this sample configuration of crankd, simply hold down the control button on your keyboard and press the C key.  Congratulations, crankd is now up and running!</p>

<h2>A more complex example</h2>

<p>Let&rsquo;s look at one of our previous situations</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet + Github = Laptop <3]]></title>
    <link href="http://garylarizza.com/blog/2013/02/15/puppet-plus-github-equals-laptop-love/"/>
    <updated>2013-02-15T00:00:00-06:00</updated>
    <id>http://garylarizza.com/blog/2013/02/15/puppet-plus-github-equals-laptop-love</id>
    <content type="html"><![CDATA[<p>Everybody wants to be special (I blame our moms). The price of special, when
it comes to IT, is time. Consider how long you&rsquo;ve spent on just your damn
PROMPT and you&rsquo;ll realize why automation gives any good sysadmin a case of
the giggles. Like the cobbler&rsquo;s kids, though, your laptop and development
environment are always the last to be visited by the automation gnomes.</p>

<p>Until now.</p>

<div class="embed-video-container"><iframe src="http://www.youtube.com/embed/YlKXRdSAZhY "></iframe></div>


<p><a href="http://www.youtube.com/watch?v=YlKXRdSAZhY">Will Farrington gave a great talk at Puppetconf 2012</a>
about managing an army of developer laptops using Puppet + some Github love
that left more than a couple of people asking for his code. That request
has unfortunately been denied.</p>

<p>Until now.</p>

<h2>Boxen, and hand-tune no more</h2>

<p><a href="http://boxen.github.com">Enter Boxen</a> (née &lsquo;The Setup&rsquo;), a full-fledged open source project from
the guys at Github that melds Puppet with your Github credentials to create a
framework for automating everything from applications, to dotfiles, and even
printers and emacs extensions (that last bit&rsquo;s a lie &ndash; no one should be using
emacs).</p>

<p>How does it work? Think &lsquo;Masterless Puppet&rsquo; (or, just a bunch of Puppet modules
that are enforced by running <code>puppet apply</code> on your local machine). Boxen not
only includes the framework ITSELF, but is a project on Github that hosts
over 75 individual modules for managing things like rbenv, homebrew, git, mysql,
postgres, riak, redis, npm, erlang, dropbox, skype, minecraft, heroku,
1password, iterm2, and much more. Odds are there&rsquo;s a module for many of the
common things you setup on your laptop. And what about things like dotfiles
that are intrinsically personal? You can create your own repository and manage
them like you would any other file/directory/symlink on the file system. The
goal is to model every little piece of your laptop that makes it &lsquo;unique&rsquo; from
everyone else until you have your entire environment managed and the hardware
becomes&hellip;well&hellip;disposable. How many times have you shied away from doing an
upgrade because some component of your laptop required you to spend countless
hours tinkering with it? If you&rsquo;ve done the time, you should do something to
make sure that you NEVER have to repeat that process manually ever again.</p>

<p>Boxen is also very self-contained. Packages and binaries that come out of
Homebrew are installed into <code>/opt/boxen/homebrew/bin</code>, frameworks like Ruby and
Node are installed into <code>/opt/boxen/{rbenv,nvm}</code>, and individual versions of
those frameworks are kept separate from your system version of those
frameworks.  These details are important when you consider that you could purge
the whole setup without having to rip out components scattered around your
system.</p>

<p>You may be reading this and thinking &ldquo;There&rsquo;s no way in hell I can use this to
manage every laptop in my organization!&rdquo;, and you&rsquo;re right. The POINT of Boxen
is that it&rsquo;s a tool written by developers for developers to automate THEIR
systems. The goal of developing is to have as little friction between the
process of writing code and deploying that code into production. A tool like
Boxen allows you to more quickly GET to the state where your laptop is ready
for you to start developing. If you want a tool to completely manage and lock
down all the laptops on your system, look to using <a href="http://www.puppetlabs.com">Puppet</a>
in agent/master mode or to a tool like <a href="https://code.google.com/p/munki/">Munki</a>
to manage all packages on your system. If you&rsquo;re interested in giving your
developers/users the freedom to manage their OWN &lsquo;boxes&rsquo; because they know best
what works for them, then Boxen is your tool.</p>

<p>There IS one catch &ndash; it&rsquo;s targeted to OS X (10.8 to be exact).</p>

<h2>Diary of an elitist</h2>

<p>I was fortunate to have early-access to Boxen in order to kick its Ruby tyres.
As someone who&rsquo;s managed Macs with Puppet before (all the way down to the
desktop/laptop level), I was embarrassed to admit that I had NOTHING about
my laptop automated. <a href="http://twitter.com/wfarr">Will</a> unlocked the project and basically said &ldquo;Have
fun, break shit, fix it, and file pull requests&rdquo; and away I went. To commit
completely to the project, I did what any sane person would do.</p>

<p>I reformatted my laptop and started entirely from scratch.</p>

<p>(Let&rsquo;s be clear here &ndash; you don&rsquo;t have to do that. Initially Will reported
problems getting Boxen running in VMs, but I never ran into an issue. I ran
Boxen in VMware Fusion 5 a number of times to make sure the changes I made
were going to do the right thing on a fresh install. I&rsquo;d recommend going down
THAT road if you&rsquo;re hesitant of immediately throwing this on your pretty
snowflake of a laptop.)</p>

<p>Installing Boxen was pretty easy &ndash; the only prerequisite was downloading
the <a href="http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/WhatsNewXcode/Articles/xcode_4_3.html">XCode Command-Line Tools</a>
(which included git), pulling down the Boxen repo, and running <code>script/boxen</code>.
It was stupid simple. What you GOT, by default, was:</p>

<ul>
<li>Homebrew</li>
<li>Git</li>
<li>Hub</li>
<li>DNSMasq w/ .dev resolver for localhost</li>
<li>NVM</li>
<li>RBenv</li>
<li>Full Disk Encryption requirement</li>
<li>NodeJS 0.4</li>
<li>NodeJS 0.6</li>
<li>NodeJS 0.8</li>
<li>Ruby 1.8.7</li>
<li>Ruby 1.9.2</li>
<li>Ruby 1.9.3</li>
<li>Ack</li>
<li>Findutils</li>
<li>GNU-Tar</li>
</ul>


<p>Remember, this is all tunable and you don&rsquo;t need to pull down ALL of these
packages, but, since it was new, I decided to install everything and sort
it out later.  Yes, the initial setup took a good number of minutes, but
think about everything that&rsquo;s being installed. In the end, I had a full Ruby
development environment with rbenv, multiple versions of Ruby, and a laptop
that could be customized without much work at all.</p>

<h2>Which end do I blow in?</h2>

<p>The readme on <a href="http://boxen.github.com">the project page for Boxen</a> describes how to clone the
project into <code>/opt/boxen/repo</code>, so that&rsquo;s the directory we&rsquo;ll be working with.
To see what will be enforced on your machine, check out <code>manifests/site.pp</code> to
see something that looks like this:</p>

<figure class='code'><figcaption><span>manifests/site.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kn">require</span> <span class="nc">boxen::environment</span>
</span><span class='line'><span class="kn">require</span> <span class="nc">homebrew::repo</span>
</span><span class='line'>
</span><span class='line'><span class="nc">Exec</span> <span class="p">{</span>
</span><span class='line'>  <span class="nt">group</span>       <span class="p">=&gt;</span> <span class="s1">&#39;staff&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">logoutput</span>   <span class="p">=&gt;</span> <span class="ss">on_failure</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">user</span>        <span class="p">=&gt;</span> <span class="nv">$luser</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>  <span class="nt">path</span> <span class="p">=&gt;</span> <span class="p">[</span>
</span><span class='line'>    <span class="s2">&quot;</span><span class="si">${boxen::config::home}</span><span class="s2">/rbenv/shims&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="s2">&quot;</span><span class="si">${boxen::config::home}</span><span class="s2">/homebrew/bin&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;/usr/bin&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;/bin&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;/usr/sbin&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;/sbin&#39;</span>
</span><span class='line'>  <span class="p">],</span>
</span><span class='line'>
</span><span class='line'>  <span class="nt">environment</span> <span class="p">=&gt;</span> <span class="p">[</span>
</span><span class='line'>    <span class="s2">&quot;HOMEBREW_CACHE=</span><span class="si">${homebrew::cachedir}</span><span class="s2">&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="s2">&quot;HOME=/Users/</span><span class="si">${::luser}</span><span class="s2">&quot;</span>
</span><span class='line'>  <span class="p">]</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">File</span> <span class="p">{</span>
</span><span class='line'>  <span class="nt">group</span> <span class="p">=&gt;</span> <span class="s1">&#39;staff&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span> <span class="p">=&gt;</span> <span class="nv">$luser</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">Package</span> <span class="p">{</span>
</span><span class='line'>  <span class="nt">provider</span> <span class="p">=&gt;</span> <span class="ss">homebrew</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">require</span>  <span class="p">=&gt;</span> <span class="nc">Class</span><span class="p">[</span><span class="s1">&#39;homebrew&#39;</span><span class="p">]</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">Repository</span> <span class="p">{</span>
</span><span class='line'>  <span class="nt">provider</span> <span class="p">=&gt;</span> <span class="ss">git</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">extra</span>    <span class="p">=&gt;</span> <span class="p">[</span>
</span><span class='line'>    <span class="s1">&#39;--recurse-submodules&#39;</span>
</span><span class='line'>  <span class="p">],</span>
</span><span class='line'>  <span class="nt">require</span>  <span class="p">=&gt;</span> <span class="nc">Class</span><span class="p">[</span><span class="s1">&#39;git&#39;</span><span class="p">]</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nc">Service</span> <span class="p">{</span>
</span><span class='line'>  <span class="nt">provider</span> <span class="p">=&gt;</span> <span class="ss">ghlaunchd</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>This is largely scaffolding setting up the Boxen environment and resource
defaults. If you&rsquo;re familiar with Puppet, this should be recognizable to you,
but for everyone else, let&rsquo;s dissect one of the resource defaults:</p>

<figure class='code'><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='puppet'><span class='line'><span class="nc">File</span> <span class="p">{</span>
</span><span class='line'>  <span class="nt">group</span> <span class="p">=&gt;</span> <span class="s1">&#39;staff&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nt">owner</span> <span class="p">=&gt;</span> <span class="nv">$luser</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>This block basically means that any file you declare with Puppet should default
to having its owner set as your username and its group set to &lsquo;staff&rsquo; (which is
standard in OS X). You can override this explicitly with a file declaration by
providing the owner or group attribute, but if you omit it then it&rsquo;s going to
default to these values.</p>

<p>The rest of the defaults are customized for Boxen&rsquo;s preferences (i.e. homebrew
will be used to install all packages unless you specify otherwise, exec
resources will log all output on failure, service resources will use githubs&rsquo;s
customized service provider, and etc&hellip;).  Now let&rsquo;s look below:</p>

<figure class='code'><figcaption><span>manifests/site.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">node</span> <span class="s">default</span> <span class="p">{</span><span class="c-Singleline"></span>
</span><span class='line'><span class="c-Singleline">  # core modules, needed for most things</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">dnsmasq</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">git</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">hub</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">nginx</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">nvm</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">ruby</span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # fail if FDE is not enabled</span>
</span><span class='line'>  <span class="kr">if</span> <span class="nv">$::root_encrypted</span> <span class="o">==</span> <span class="ss">false</span> <span class="p">{</span>
</span><span class='line'>    <span class="nf">fail</span><span class="p">(</span><span class="s1">&#39;Please enable full disk encryption and try again&#39;</span><span class="p">)</span>
</span><span class='line'>  }<span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # node versions</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">nodejs::0-4</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">nodejs::0-6</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">nodejs::0-8</span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # default ruby versions</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">ruby::1-8-7</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">ruby::1-9-2</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">ruby::1-9-3</span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # common, useful packages</span>
</span><span class='line'>  <span class="nc">package</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span>
</span><span class='line'>      <span class="s1">&#39;ack&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;findutils&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;gnu-tar&#39;</span>
</span><span class='line'>    <span class="p">]:</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${boxen::config::srcdir}</span><span class="s2">/our-boxen&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">link</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">target</span> <span class="p">=&gt;</span> <span class="nv">$boxen::config::repodir</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>These are the things that Boxen has chosen to enforce &lsquo;out of the box&rsquo;. Knowing
that Boxen was designed so that developers could customize their &lsquo;boxes&rsquo;
THEMSELVES, it makes sense that there&rsquo;s not much that&rsquo;s being enforced on
everyone. In fact, the most significant thing being &lsquo;thrust&rsquo; upon you is the
fact that the machine must have full disk encryption enabled (which is a
good idea anyways).</p>

<p>If you want to pare down what Boxen gives you by default, you can choose to
comment out lines providing, for example, nvm and nodejs versions (if you don&rsquo;t
use node.js in your environment). I&rsquo;m a Ruby developer, so all the Ruby builds
(and rbenv) are very helpful to me, but you could also remove those if you were
so inclined. The point is that this file contains the &lsquo;knobs&rsquo; to dial your base
Boxen setup up or down.</p>

<h2>Customizing (or, my dotfiles are better than yours)</h2>

<p>The whole point of Boxen is to customize your laptop and keep its customization
automated. To do this, we&rsquo;re going to need to make some Puppet class files.</p>

<p><strong> CAUTION: PUPPET AHEAD </strong></p>

<p>If you&rsquo;ve not had experience with Puppet before, <a href="http://docs.puppetlabs.com/learning/">I can&rsquo;t recommend the learning
Puppet series enough</a>. In the vein of &ldquo;Puppet now, learn later&rdquo;,
I&rsquo;m going to give you Puppet code that works for ME and only explain the trickier
bits.</p>

<p>Boxen has some &lsquo;magic&rsquo; code that&rsquo;s going to automatically look for a class
called <code>people::&lt;github username&gt;</code>, and so I&rsquo;m going to create a file in
<code>modules/people/manifests</code> called <code>glarizza.pp</code>.  This file will contain Puppet
code specific to MY laptop(s).  Here&rsquo;s a snippit of that file:</p>

<figure class='code'><figcaption><span>modules/people/manifests/glarizza.pp </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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">people::glarizza</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">notify</span> <span class="p">{</span> <span class="s1">&#39;class people::glarizza declared&#39;</span><span class="p">:</span> <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # Changes the default shell to the zsh version we get from Homebrew</span>
</span><span class='line'><span class="c-Singleline">  # Uses the osx_chsh type out of boxen/puppet-osx</span>
</span><span class='line'>  <span class="nc">osx_chsh</span> <span class="p">{</span> <span class="nv">$::luser</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">shell</span>   <span class="p">=&gt;</span> <span class="s1">&#39;/opt/boxen/homebrew/bin/zsh&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Package</span><span class="p">[</span><span class="s1">&#39;zsh&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file_line</span> <span class="p">{</span> <span class="s1">&#39;add zsh to /etc/shells&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">path</span>    <span class="p">=&gt;</span> <span class="s1">&#39;/etc/shells&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">line</span>    <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${boxen::config::homebrewdir}</span><span class="s2">/bin/zsh&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Package</span><span class="p">[</span><span class="s1">&#39;zsh&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  ##################################</span>
</span><span class='line'><span class="c-Singleline">  ## Facter, Puppet, and Envpuppet##</span>
</span><span class='line'><span class="c-Singleline">  ##################################</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">repository</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${::boxen_srcdir}</span><span class="s2">/puppet&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppetlabs/puppet&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">repository</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${::boxen_srcdir}</span><span class="s2">/facter&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;puppetlabs/facter&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s1">&#39;/bin/envpuppet&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>  <span class="p">=&gt;</span> <span class="ss">link</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">mode</span>    <span class="p">=&gt;</span> <span class="s1">&#39;0755&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">target</span>  <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${::boxen_srcdir}</span><span class="s2">/puppet/ext/envpuppet&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Repository</span><span class="p">[</span><span class="s2">&quot;</span><span class="si">${::boxen_srcdir}</span><span class="s2">/puppet&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>The notify resource is only to prove that when you run Boxen that this class
is being declared &ndash; it only displays a message to the console when you run
the <code>boxen</code> binary.</p>

<p>The <code>osx_chsh</code> resource is a custom defined type that Github has created to
ensure a line shows up in <code>/etc/shells</code> as an acceptable shell. Because
Boxen installs zsh from homebrew into <code>/opt/boxen/homebrew</code>, we need to ensure
that <code>/etc/shells</code> is correct. Note the syntax of <code>$boxen::config::homebrewdir</code>
which refers to a variable called <code>$homebrewdir</code> in the <code>boxen::config</code> class.</p>

<p>Next, I&rsquo;ve setup a couple of resources to make sure the <code>puppet</code> and <code>facter</code>
repositories are installed on my system. Github has also developed a lightweight
<code>repository</code> resource that will simply ensure that a repo is cloned at a location
on disk. <code>$::boxen_srcdir</code> is one of the <a href="http://docs.puppetlabs.com/guides/custom_facts.html">custom Facter facts</a> that
Boxen provides in <code>shared/boxen/lib/facter/boxen.rb</code> in the Boxen repository.</p>

<p>The file resource sets up a symlink from <code>/bin/envpuppet</code> to
<code>/Users/glarizza/src/puppet/ext/envpuppet</code> on my system. The attributes should
be pretty self-explanatory, but the newest attribute of <code>require</code> says that the
<code>repository</code> resource must come BEFORE this file resource is declared. This is
a demonstration of <a href="http://docs.puppetlabs.com/learning/ordering.html">Puppet&rsquo;s ordering metaparameters</a> that are described
in the <a href="http://docs.puppetlabs.com/learning/">Learning Puppet</a> series.</p>

<p>Since we briefly touched on <code>$::boxen_srcdir</code>, what are some other custom facts
that come out of <code>shared/boxen/lib/facter/boxen.rb</code>?</p>

<figure class='code'><figcaption><span>shared/boxen/lib/facter/boxen.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>
<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="nb">require</span> <span class="s2">&quot;json&quot;</span>
</span><span class='line'><span class="nb">require</span> <span class="s2">&quot;boxen/config&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="n">config</span>   <span class="o">=</span> <span class="ss">Boxen</span><span class="p">:</span><span class="ss">:Config</span><span class="o">.</span><span class="n">load</span>
</span><span class='line'><span class="n">facts</span>    <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'><span class="n">factsdir</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span> <span class="n">config</span><span class="o">.</span><span class="n">homedir</span><span class="p">,</span> <span class="s2">&quot;config&quot;</span><span class="p">,</span> <span class="s2">&quot;facts&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="n">facts</span><span class="o">[</span><span class="s2">&quot;github_login&quot;</span><span class="o">]</span>   <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">login</span>
</span><span class='line'><span class="n">facts</span><span class="o">[</span><span class="s2">&quot;github_email&quot;</span><span class="o">]</span>   <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">email</span>
</span><span class='line'><span class="n">facts</span><span class="o">[</span><span class="s2">&quot;github_name&quot;</span><span class="o">]</span>    <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">name</span>
</span><span class='line'>
</span><span class='line'><span class="n">facts</span><span class="o">[</span><span class="s2">&quot;boxen_home&quot;</span><span class="o">]</span>     <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">homedir</span>
</span><span class='line'><span class="n">facts</span><span class="o">[</span><span class="s2">&quot;boxen_srcdir&quot;</span><span class="o">]</span>   <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">srcdir</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="n">config</span><span class="o">.</span><span class="n">respond_to?</span> <span class="ss">:reponame</span>
</span><span class='line'>  <span class="n">facts</span><span class="o">[</span><span class="s2">&quot;boxen_reponame&quot;</span><span class="o">]</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">reponame</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">facts</span><span class="o">[</span><span class="s2">&quot;luser&quot;</span><span class="o">]</span>          <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">user</span>
</span><span class='line'>
</span><span class='line'><span class="no">Dir</span><span class="o">[</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">config</span><span class="o">.</span><span class="n">homedir</span><span class="si">}</span><span class="s2">/config/facts/*.json&quot;</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
</span><span class='line'>  <span class="n">facts</span><span class="o">.</span><span class="n">merge!</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span> <span class="no">File</span><span class="o">.</span><span class="n">read</span> <span class="n">file</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">facts</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span> <span class="no">Facter</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="p">{</span> <span class="n">setcode</span> <span class="p">{</span> <span class="n">v</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>This file will also give you <code>$::luser</code>, which will evaluate out to your system
username, and <code>$::github_name</code>, which is equivalent to your Github username
(note that this is what Boxen uses to find your class file in
<code>modules/people/manifests</code>). If you&rsquo;re looking for all the other values set by
these custom facts, check out <code>config/boxen/defaults.json</code> after you run Boxen.</p>

<h2>Using modules out of the Boxen namespace</h2>

<p>Not only is Boxen its own project, but it&rsquo;s a <a href="http://github.com/boxen">separate organization on Github
that hosts a number of Puppet modules</a>. Some of these modules are
pretty simple (a single resource to install a package), but the point is that
they&rsquo;ve been provided FOR you &ndash; so use, fork, and improve them (but most of
all, submit pull requests). The way you use them with Boxen may not be readily
clear, so let&rsquo;s walk through that with a simple module for installing Google
Chrome.</p>

<ol>
<li>Add the module to your Puppetfile</li>
<li>Classify the module in your Puppet setup</li>
<li>Run Boxen</li>
</ol>


<h4>Add the module to your Puppetfile</h4>

<p>Boxen uses a tool called <a href="https://github.com/rodjek/librarian-puppet">librarian-puppet</a> to source and install Puppet
modules from Github. Librarian-puppet uses the <code>Puppetfile</code> file in the root of
the Boxen repo to install modules.  Let&rsquo;s look at a couple of lines in that
file:</p>

<figure class='code'><figcaption><span>Puppetfile </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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;boxen&quot;</span><span class="p">,</span>    <span class="s2">&quot;0.1.8&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-boxen&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;dnsmasq&quot;</span><span class="p">,</span>  <span class="s2">&quot;0.0.1&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-dnsmasq&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;git&quot;</span><span class="p">,</span>      <span class="s2">&quot;0.0.3&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-git&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;hub&quot;</span><span class="p">,</span>      <span class="s2">&quot;0.0.1&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-hub&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;homebrew&quot;</span><span class="p">,</span> <span class="s2">&quot;0.0.17&quot;</span><span class="p">,</span> <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-homebrew&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;inifile&quot;</span><span class="p">,</span>  <span class="s2">&quot;0.0.1&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-inifile&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;nginx&quot;</span><span class="p">,</span>    <span class="s2">&quot;0.0.2&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-nginx&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;nodejs&quot;</span><span class="p">,</span>   <span class="s2">&quot;0.0.2&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-nodejs&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;nvm&quot;</span><span class="p">,</span>      <span class="s2">&quot;0.0.5&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-nvm&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;ruby&quot;</span><span class="p">,</span>     <span class="s2">&quot;0.4.0&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-ruby&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;stdlib&quot;</span><span class="p">,</span>   <span class="s2">&quot;3.0.0&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;puppetlabs/puppetlabs-stdlib&quot;</span>
</span><span class='line'><span class="n">mod</span> <span class="s2">&quot;sudo&quot;</span><span class="p">,</span>     <span class="s2">&quot;0.0.1&quot;</span><span class="p">,</span>  <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-sudo&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>This evaluates out to the following syntax:</p>

<figure class='code'><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=''><span class='line'>mod, &lt;module name&gt;, &lt;version or tag&gt;, &lt;source&gt;</span></code></pre></td></tr></table></div></figure>


<p>The <strong>HARDEST</strong> thing about this file is finding the version number of modules
on Github (HINT: it&rsquo;s a tag). Once you&rsquo;re given that information, it&rsquo;s easy to
pull up a module on Github, look at its tags, and then fill out the file. Let&rsquo;s
do that with a line for the Chrome module:</p>

<figure class='code'><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='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;chrome&quot;</span><span class="p">,</span>     <span class="s2">&quot;0.0.2&quot;</span><span class="p">,</span>   <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;boxen/puppet-chrome&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Classify the module in your Puppet setup</h4>

<p>In the previous section, we created <code>modules/people/manifests/&lt;github
username&gt;.pp</code>.  We COULD continue to fill this file with a ton of resources,
but I tend to like to separate out resources into separate subclasses. <a href="http://docs.puppetlabs.com/learning/modules1.html#manifests-namespacing-and-autoloading">Puppet
has module naming conventions to ensure that it can FIND your
subclasses</a>, so I recommend browsing that guide before randomly
naming files (<strong>HINT:</strong> Filenames ARE important and DO matter here). I want to
create a <code>people::glarizza::applications</code> subclass, so I need to do the
following:</p>

<figure class='code'><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=''><span class='line'>## YES, make sure to replace YOUR USERNAME for 'glarizza'
</span><span class='line'>$ cd /opt/boxen/repo
</span><span class='line'>$ mkdir -p modules/people/manifests/glarizza
</span><span class='line'>$ vim modules/people/manifests/glarizza/applications.pp</span></code></pre></td></tr></table></div></figure>


<p>It&rsquo;s totally fine that there&rsquo;s a <code>glarizza</code> directory aside the <code>glarizza.pp</code>
file &ndash; this is intentional and desired. Puppet&rsquo;s not going to automatically
declare anything in the <code>people::glarizza::applications</code> class until we TELL it
to, so let&rsquo;s open <code>modules/people/manifests/glarizza.pp</code> and add the following
line at the top:</p>

<figure class='code'><figcaption><span>modules/people/manifests/glarizza.pp </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='puppet'><span class='line'><span class="kn">include</span> <span class="nc">people::glarizza::applications</span>
</span></code></pre></td></tr></table></div></figure>


<p>That tells Puppet to find the <code>people::glarizza::applications</code> class and make
sure it &lsquo;does&rsquo; everything in that file. Now, let&rsquo;s create the
<code>people::glarizza::applications</code> class:</p>

<figure class='code'><figcaption><span>modules/people/manifests/glarizza/applications.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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="kd">class</span> <span class="nc">people::glarizza::applications</span> <span class="p">{</span>
</span><span class='line'>  <span class="kn">include</span> <span class="nc">chrome</span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>Yep, all it takes is one line to include the module we will get from Boxen.
Because of the way Boxen works, it will consult the Puppetfile FIRST, pull down
any modules that are in the Puppetfile but NOT on the system, drop them into
place so Puppet can find them, and then run Puppet normally.</p>

<h4>Run Boxen</h4>

<p>Once you have Boxen setup, you can just run <code>boxen</code> from the command line to
have it enforce your configuration. By default, if there are any errors, it
will log them as Github Issues on your fork of the main Boxen repository (this
can be disabled with <code>boxen --no-issue</code>). As you&rsquo;re just getting started,
don&rsquo;t worry about the errors. The good news is that once you fix things and
perform a successful Boxen run, it will automatically close all open issues.
If everything went well, you should now have Google Chrome in your
<code>/Applications</code> directory!</p>

<h2>¡Más Puppet!</h2>

<p>You&rsquo;ll find as you start customizing all the things that you&rsquo;re usually
managing one of the following resources:</p>

<ol>
<li>Packages</li>
<li>Files</li>
<li>Repositories</li>
<li>Plist files</li>
</ol>


<p>We&rsquo;ve covered managing a repository and a file, but let&rsquo;s look at a couple of
the other popular resources:</p>

<h3>Packages are annoying</h3>

<p>I would be willing to bet that most of the things you end up managing will be
packages. Using Puppet with Boxen, you have the ability to install four
different kinds of packages:</p>

<ol>
<li>Applications inside a DMG</li>
<li>Installer packages inside a DMG</li>
<li>Homebrew Packages</li>
<li>Applications inside a .zip file</li>
</ol>


<p>Here&rsquo;s an example of every type of package installer:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="c-Singleline">  # Application in a DMG</span>
</span><span class='line'>  <span class="nc">package</span> <span class="p">{</span> <span class="s1">&#39;Gephi&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>   <span class="p">=&gt;</span> <span class="ss">installed</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">source</span>   <span class="p">=&gt;</span> <span class="s1">&#39;https://launchpadlibrarian.net/98903476/gephi-0.8.1-beta.dmg&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">provider</span> <span class="p">=&gt;</span> <span class="ss">appdmg</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # Installer in a DMG</span>
</span><span class='line'>  <span class="nc">package</span> <span class="p">{</span> <span class="s1">&#39;Virtualbox&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">installed</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;http://download.virtualbox.org/virtualbox/4.1.22/VirtualBox-4.1.23-80870-OSX.dmg&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">provider</span> <span class="p">=&gt;</span> <span class="ss">pkgdmg</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # Homebrew Package</span>
</span><span class='line'>  <span class="nc">package</span> <span class="p">{</span> <span class="s1">&#39;tmux&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">installed</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # Application in a .zip</span>
</span><span class='line'>  <span class="nc">package</span> <span class="p">{</span> <span class="s1">&#39;Github for Mac&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>   <span class="p">=&gt;</span> <span class="ss">installed</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">source</span>   <span class="p">=&gt;</span> <span class="s1">&#39;https://github-central.s3.amazonaws.com/mac%2FGitHub%20for%20Mac%2069.zip&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">provider</span> <span class="p">=&gt;</span> <span class="ss">compressed_app</span>
</span><span class='line'>  <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Notice that the only thing that changes among these resources is the <code>provider</code>
attribute. Remember from before that Boxen sets the default package provider to
be &lsquo;homebrew&rsquo;, so for &lsquo;tmux&rsquo; I omitted the provider attribute to utilize the
default. Also, the <code>ensure</code> attribute is defaulted to &lsquo;installed&rsquo;, so
technically I could remove it&hellip;but I tend to prefer to use it for people who
will be reading my code later.</p>

<p>There&rsquo;s no provider for .pkg files. Why? Well, packages on OS X are
either bundles or flat-packages. Bundles LOOK like individual files, but they&rsquo;re
actually folders that contain everything necessary to expand and install the
package. Flat packages are just that &ndash; an actual file that ends in .pkg that
can be expanded to install whatever you want. Bundle packages are pretty
common, but they&rsquo;re also hard for curl to download them (being that it&rsquo;s just
a folder full of files) &ndash; this is why most installer packages you encounter on
OS X are going to be enclosed in a .dmg Disk Image.</p>

<p>So which provider will you use? Well, if your file ends in .dmg then you&rsquo;re
going to be using either the <code>pkgdmg</code> or <code>appdmg</code> provider. How do you know
which to use? Expand the .dmg file and look inside it. If it contains an
application ending in .app that simply needs dragged into the <code>/Applications</code>
folder on disk, then chose the <code>appdmg</code> provider (that&rsquo;s essentially all it
does &ndash; expand the .dmg file and ditto the .app file into <code>/Applications</code>).
If the disk image contains a .pkg package installer, then you&rsquo;ll chose the
<code>pkgdmg</code> provider (which expands the .dmg file and uses <code>installer</code> to install
the contents of the .pkg file silently in the background). If your file is a
.zip file containing an Application (.app file), then you can use Github&rsquo;s
custom <code>compressed_app</code> provider that will unzip the file and ditto the app
into <code>/Applications</code>. Finally, if you want to install a package from Homebrew,
then the <code>homebrew</code> provider is pretty self-explanatory here.</p>

<p>(NOTE: There is ONE more package provider I haven&rsquo;t covered here &ndash; the <code>macports</code>
provider. It requires <a href="http://www.macports.org">Macports</a> to be installed on your
system, and will use it to install a package. Macports vs. Homebrew arguments
notwithstanding, if you&rsquo;re into Macports then there&rsquo;s a provider for you.)</p>

<h3>Plists: because why NOT XML :\</h3>

<p>Apple falls somewhere between &ldquo;the registry&rdquo; and &ldquo;config files&rdquo; on the timeline
of tweaking system settings. Most settings are locked up in plist files that
can be managed by hand or with <code>plistbuddy</code> or <code>defaults</code>. A couple of people
have saved their customizations in with their dotfiles (<a href="https://github.com/holman/dotfiles/blob/master/osx/set-defaults.sh">Zach Holman has an example here</a>),
but Puppet is a great way for managing individual keys in your plist files.
<a href="http://github.com/glarizza/puppet-property_list_key">I&rsquo;ve written a module that will manage any number of keys in a plist
file</a>.  You can modify your <code>Puppetfile</code> to
make sure Boxen picks up my module by adding the following line:</p>

<figure class='code'><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='ruby'><span class='line'><span class="n">mod</span> <span class="s2">&quot;property_list_key&quot;</span><span class="p">,</span>  <span class="s2">&quot;0.1.0&quot;</span><span class="p">,</span>   <span class="ss">:github_tarball</span> <span class="o">=&gt;</span> <span class="s2">&quot;glarizza/puppet-property_list_key&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Next, you&rsquo;ll need to add resources to your classes:</p>

<figure class='code'><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>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="c-Singleline">  # Disable Gatekeeper so you can install any package you want</span>
</span><span class='line'>  <span class="nc">property_list_key</span> <span class="p">{</span> <span class="s1">&#39;Disable Gatekeeper&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">path</span>   <span class="p">=&gt;</span> <span class="s1">&#39;/var/db/SystemPolicy-prefs.plist&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">key</span>    <span class="p">=&gt;</span> <span class="s1">&#39;enabled&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value</span>  <span class="p">=&gt;</span> <span class="s1">&#39;no&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">$my_homedir</span> <span class="o">=</span> <span class="s2">&quot;/Users/</span><span class="si">${::luser}</span><span class="s2">&quot;</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # NOTE: Dock prefs only take effect when you restart the dock</span>
</span><span class='line'>  <span class="nc">property_list_key</span> <span class="p">{</span> <span class="s1">&#39;Hide the dock&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>     <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">path</span>       <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_homedir}</span><span class="s2">/Library/Preferences/com.apple.dock.plist&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">key</span>        <span class="p">=&gt;</span> <span class="s1">&#39;autohide&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value</span>      <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value_type</span> <span class="p">=&gt;</span> <span class="s1">&#39;boolean&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">notify</span>     <span class="p">=&gt;</span> <span class="nc">Exec</span><span class="p">[</span><span class="s1">&#39;Restart the Dock&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">property_list_key</span> <span class="p">{</span> <span class="s1">&#39;Align the Dock Left&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>     <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">path</span>       <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_homedir}</span><span class="s2">/Library/Preferences/com.apple.dock.plist&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">key</span>        <span class="p">=&gt;</span> <span class="s1">&#39;orientation&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value</span>      <span class="p">=&gt;</span> <span class="s1">&#39;left&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">notify</span>     <span class="p">=&gt;</span> <span class="nc">Exec</span><span class="p">[</span><span class="s1">&#39;Restart the Dock&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">property_list_key</span> <span class="p">{</span> <span class="s1">&#39;Lower Right Hotcorner - Screen Saver&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>     <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">path</span>       <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_homedir}</span><span class="s2">/Library/Preferences/com.apple.dock.plist&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">key</span>        <span class="p">=&gt;</span> <span class="s1">&#39;wvous-br-corner&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value</span>      <span class="p">=&gt;</span> <span class="m">10</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value_type</span> <span class="p">=&gt;</span> <span class="s1">&#39;integer&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">notify</span>     <span class="p">=&gt;</span> <span class="nc">Exec</span><span class="p">[</span><span class="s1">&#39;Restart the Dock&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">property_list_key</span> <span class="p">{</span> <span class="s1">&#39;Lower Right Hotcorner - Screen Saver - modifier&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>     <span class="p">=&gt;</span> <span class="ss">present</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">path</span>       <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_homedir}</span><span class="s2">/Library/Preferences/com.apple.dock.plist&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">key</span>        <span class="p">=&gt;</span> <span class="s1">&#39;wvous-br-modifier&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value</span>      <span class="p">=&gt;</span> <span class="m">0</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">value_type</span> <span class="p">=&gt;</span> <span class="s1">&#39;integer&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">notify</span>     <span class="p">=&gt;</span> <span class="nc">Exec</span><span class="p">[</span><span class="s1">&#39;Restart the Dock&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">exec</span> <span class="p">{</span> <span class="s1">&#39;Restart the Dock&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">command</span>     <span class="p">=&gt;</span> <span class="s1">&#39;/usr/bin/killall -HUP Dock&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">refreshonly</span> <span class="p">=&gt;</span> <span class="ss">true</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s1">&#39;Dock Plist&#39;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>  <span class="p">=&gt;</span> <span class="ss">file</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="p">[</span>
</span><span class='line'>                 <span class="nc">Property_list_key</span><span class="p">[</span><span class="s1">&#39;Lower Right Hotcorner - Screen Saver - modifier&#39;</span><span class="p">],</span>
</span><span class='line'>                 <span class="nc">Property_list_key</span><span class="p">[</span><span class="s1">&#39;Hide the dock&#39;</span><span class="p">],</span>
</span><span class='line'>                 <span class="nc">Property_list_key</span><span class="p">[</span><span class="s1">&#39;Align the Dock Left&#39;</span><span class="p">],</span>
</span><span class='line'>                 <span class="nc">Property_list_key</span><span class="p">[</span><span class="s1">&#39;Lower Right Hotcorner - Screen Saver&#39;</span><span class="p">],</span>
</span><span class='line'>                 <span class="nc">Property_list_key</span><span class="p">[</span><span class="s1">&#39;Lower Right Hotcorner - Screen Saver - modifier&#39;</span><span class="p">],</span>
</span><span class='line'>               <span class="p">],</span>
</span><span class='line'>    <span class="nt">path</span>    <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_homedir}</span><span class="s2">/Library/Preferences/com.apple.dock.plist&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">mode</span>    <span class="p">=&gt;</span> <span class="s1">&#39;0600&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">notify</span>     <span class="p">=&gt;</span> <span class="nc">Exec</span><span class="p">[</span><span class="s1">&#39;Restart the Dock&#39;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The important attributes are:</p>

<ol>
<li><code>path</code>: The path to the plist file on disk</li>
<li><code>key</code>: The individual KEY in the plist file you want to manage</li>
<li><code>value</code>: The value that the key should have in the plist file</li>
<li><code>value_type</code>: The datatype the value should be (defaults to <code>string</code>, but
could also be <code>array</code>, <code>hash</code>, <code>boolean</code>, or <code>integer</code>)</li>
</ol>


<p>You MUST pass a path, key, and value or Puppet will throw an error.</p>

<p>The first resource above sets Gatekeeper in 10.8 and allows you to install
packages from the web that HAVEN&rsquo;T been signed (in 10.8, Apple won&rsquo;t allow you
to install unsigned packages or anything outside of the App Store without
enabling this setting).</p>

<p>All of the other resources relate to making changes to the Dock. Because of the
way the Dock is managed, you must <code>HUP</code> its process when making changes to your
dock plist before they take effect. Also, the dock plist has to be owned by
you or else the changes won&rsquo;t take effect. Every dock plist resource has
a <code>notify</code> metaparameter which means &ldquo;any time this resource changes, run this
exec resource&rdquo;. That exec resource is a simple command that <code>HUP</code>s the dock
process.  It will ONLY be run if a resource notifies it &ndash; so if no changes are
made in a Puppet run then the command won&rsquo;t fire. Finally, the file resource to
manage the dock plist ensures that permissions are set (and notifies the exec
in case it needs to CHANGE permissions).</p>

<p>Again, this is purely dealing with Puppet &ndash; but plists are a major part of OS
X and you&rsquo;ll be dealing with them regularly!</p>

<h3>But seriously, dotfiles</h3>

<p>I know I&rsquo;ve joked about it a couple of times, but getting your dotfiles into
their correct location is a quick win. The secret is to lock them all up in
a repository, and then symlink them where you need them. Let&rsquo;s look at that:</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class='puppet'><span class='line'><span class="c-Singleline">  # My dotfile repository</span>
</span><span class='line'>  <span class="nc">repository</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">source</span> <span class="p">=&gt;</span> <span class="s1">&#39;glarizza/dotfiles&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;</span><span class="si">${my_homedir}</span><span class="s2">/.tmux.conf&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>  <span class="p">=&gt;</span> <span class="ss">link</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">mode</span>    <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">target</span>  <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles/tmux.conf&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Repository</span><span class="p">[</span><span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;/Users/</span><span class="si">${my_username}</span><span class="s2">/.zshrc&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>  <span class="p">=&gt;</span> <span class="ss">link</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">mode</span>    <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">target</span>  <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles/zshrc&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Repository</span><span class="p">[</span><span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;/Users/</span><span class="si">${my_username}</span><span class="s2">/.vimrc&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span> <span class="p">=&gt;</span> <span class="ss">link</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">mode</span>   <span class="p">=&gt;</span> <span class="s1">&#39;0644&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">target</span> <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles/vimrc&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Repository</span><span class="p">[</span><span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/dotfiles&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span><span class="c-Singleline"></span>
</span><span class='line'>
</span><span class='line'><span class="c-Singleline">  # Yes, oh-my-zsh. Judge me.</span>
</span><span class='line'>  <span class="nc">file</span> <span class="p">{</span> <span class="s2">&quot;/Users/</span><span class="si">${my_username}</span><span class="s2">/.oh-my-zsh&quot;</span><span class="p">:</span>
</span><span class='line'>    <span class="nt">ensure</span>  <span class="p">=&gt;</span> <span class="ss">link</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">target</span>  <span class="p">=&gt;</span> <span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/oh-my-zsh&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">require</span> <span class="p">=&gt;</span> <span class="nc">Repository</span><span class="p">[</span><span class="s2">&quot;</span><span class="si">${my_sourcedir}</span><span class="s2">/oh-my-zsh&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>It&rsquo;s worth mentioning that Puppet does not do things procedurally. Just because
the dotfiles repository is listed before every symlink DOES NOT mean that Puppet
will evaluate and declare it first. You&rsquo;ll need to specify order here, and that&rsquo;s
what the <code>require</code> metaparameter does.</p>

<p>Based on what I&rsquo;ve already shown you, this code block should be very simple to
follow. Because I&rsquo;m using symlinks, the dotfiles should always be current.
Because the dotfiles are under revision control, updating them all is as simple
as making commits and updating your repository. If you&rsquo;ve ever had to migrate
these files to a new VM/machine, then you know how full of win this block of
code is.</p>

<h2>Don&rsquo;t sweat petty (or pet sweaty)</h2>

<p>When I show sysadmins/developers automation like this, they usually want to
apply it to the HARDEST part of their day-job IMMEDIATELY. That&rsquo;s a somewhat
rational reaction, but it&rsquo;s not going to give you the results you want. The
cool thing ABOUT Boxen and Puppet is that it&rsquo;s going to remove those little
annoyances in your day that slowly sap your time. START by tackling those
small annoyances to remove them and build your confidence (like the dotfiles
example above). Yeah, you&rsquo;ll only save a couple of minutes a day, but it
grows exponentially. Also, when you solve a problem during the course of your
day, MANAGE it with Boxen by putting it in your Puppet class (then, test it
out on a VM or another machine to make sure it does what you expect).</p>

<p>Don&rsquo;t worry that you&rsquo;re not saving the world with a massive Puppet class &ndash;
sometimes the secret to happiness is opening iTerm on a new machine and
seeing your finely-crafted prompt shining in its cornflower-blue glory.</p>

<h2>Now show me some cool stuff</h2>

<p>So that&rsquo;s a quick tour of the basics of Boxen and the kinds of things you can
do from the start.  I&rsquo;m really excited for everyone to get their hands on Boxen
and do more cool stuff with Puppet. I&rsquo;ve done a bunch of work with Puppet for
OS X, and that&rsquo;s enough to know that there&rsquo;s still PLENTY that can be
improved in the codebase.  A giant THANK YOU to <a href="http://twitter.com/jbarnette">John Barnette</a>, <a href="http://twitter.com/wfarr">Will Farrington</a>,
and the entire Github Boxen team for all their work on this
tool (and letting me tinker with it before it hit the general public)! Feel
free to comment below, email me (gary at puppetlabs), or <a href="http://twitter.com/glarizza">yell at me on Twitter</a>
for more information!</p>
]]></content>
  </entry>
  
</feed>
