<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title type="text" xml:lang="en">Tim Sharpe - Puppet</title>
    <link type="application/atom+xml" href="http://bombasticmonkey.com/feeds/puppet.xml" rel="self"/>
    <link type="text/html" href="http://bombasticmonkey.com" rel="alternate"/>
    <updated>2012-03-02T17:51:59+11:00</updated>
    <id>http://bombasticmonkey.com/</id>
    <author>
        <name>Tim Sharpe</name>
        <email>tim@sharpe.id.au</email>
    </author>
    <rights>Copyright (c) 2010-2011 Tim Sharpe</rights>
    
    <entry>
        <title>Automatically Test Your Puppet Modules With rspec-puppet, puppet-lint And Travis CI</title>
        <link href="http://bombasticmonkey.com/2012/03/02/automatically-test-your-puppet-modules-with-travis-ci/"/>
        <updated>2012-03-02T00:00:00+11:00</updated>
        <id>http://bombasticmonkey.com/2012/03/02/automatically-test-your-puppet-modules-with-travis-ci/</id>
        <content type="html">&lt;p&gt;I&amp;#8217;m going to assume you&amp;#8217;ve got a Puppet module already on GitHub. To save messing around with bundler on your local machine, I recommend installing &lt;code&gt;puppet-lint&lt;/code&gt; and &lt;code&gt;rspec-puppet&lt;/code&gt; as system gems while you&amp;#8217;re getting this all set up.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='console'&gt;1
2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gp'&gt;$&lt;/span&gt; gem install puppet-lint
&lt;span class='gp'&gt;$&lt;/span&gt; gem install rspec-puppet
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h2 id='rspecpuppet'&gt;rspec-puppet&lt;/h2&gt;

&lt;p&gt;First of all, let&amp;#8217;s create a directory structure for your spec files&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='console'&gt;1
2
3
4
5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gp'&gt;$&lt;/span&gt; mkdir -p spec/classes spec/defines spec/fixtures/manifests
&lt;span class='gp'&gt;$&lt;/span&gt; mkdir -p spec/fixtures/modules/&amp;lt;your module name&amp;gt;
&lt;span class='gp'&gt;$&lt;/span&gt; &lt;span class='nb'&gt;cd &lt;/span&gt;spec/fixtures/modules/&amp;lt;your module name&amp;gt;
&lt;span class='gp'&gt;$&lt;/span&gt; touch spec/fixtures/manifests/init.pp
&lt;span class='gp'&gt;$&lt;/span&gt; &lt;span class='k'&gt;for &lt;/span&gt;i in files lib manifests templates; &lt;span class='k'&gt;do &lt;/span&gt;ln -s ../../../../&lt;span class='nv'&gt;$i&lt;/span&gt; &lt;span class='nv'&gt;$i&lt;/span&gt;; &lt;span class='k'&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;If you&amp;#8217;re wondering about that last line, we symlink the contents of the module into &lt;code&gt;spec/fixtures/modules/&amp;lt;your module name&amp;gt;&lt;/code&gt; so that we can trick Puppet&amp;#8217;s autoloader when running the specs.&lt;/p&gt;

&lt;p&gt;Next, we need to configure rspec-puppet, so create &lt;code&gt;spec/spec_helper.rb&lt;/code&gt; with the following contents&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6
7
8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec-puppet&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;fixture_path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;..&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;..&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;fixtures&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;

&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;module_path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;fixture_path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;modules&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;manifest_dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;fixture_path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;manifests&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Now, all we need is a Rake task to fire up the tests. Create a &lt;code&gt;Rakefile&lt;/code&gt; in the root directory of your module with the following contents&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6
7&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;

&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec/core/rake_task&amp;#39;&lt;/span&gt;

&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Core&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;RakeTask&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:spec&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pattern&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec/*/*_spec.rb&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;You can now run &lt;code&gt;rake spec&lt;/code&gt; to run your &lt;code&gt;rspec-puppet&lt;/code&gt; tests&lt;/p&gt;

&lt;h2 id='puppetlint'&gt;puppet-lint&lt;/h2&gt;

&lt;p&gt;Next up, we&amp;#8217;ll also get some automatic lint testing of your manifests going to ensure you&amp;#8217;re writing manifests that comply with the Puppet Labs style guide. This is simply a matter adding the following line near the top of your &lt;code&gt;Rakefile&lt;/code&gt;&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;puppet-lint/tasks/puppet-lint&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;You can now run &lt;code&gt;rake lint&lt;/code&gt; to run puppet-lint over your manifests.&lt;/p&gt;

&lt;h2 id='travis_ci'&gt;Travis CI&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://travis-ci.org'&gt;Travis CI&lt;/a&gt; is a wonderful free continuous integration service that integrates with &lt;a href='https://github.com'&gt;GitHub&lt;/a&gt;, running whatever tests you want against your code every time you push.&lt;/p&gt;

&lt;p&gt;To get Travis CI automatically testing your module you need to add a couple of files to the root directory of your module.&lt;/p&gt;

&lt;p&gt;First, create a &lt;code&gt;Gemfile&lt;/code&gt; which tells bundler which ruby gems your tests need in order to run. If your module needs any additional gems, just add them to the bottom of this file.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='n'&gt;source&lt;/span&gt; &lt;span class='ss'&gt;:rubygems&lt;/span&gt;

&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='no'&gt;ENV&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;key?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;PUPPET_VERSION&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;puppetversion&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;= &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='no'&gt;ENV&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;PUPPET_VERSION&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;else&lt;/span&gt;
  &lt;span class='n'&gt;puppetversion&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;&amp;gt;= 2.7&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;puppet-lint&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec-puppet&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;puppet&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;puppetversion&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;We also need to create &lt;code&gt;.travis.yml&lt;/code&gt; which holds our Travis CI test config.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='yaml'&gt;1
2
3
4
5
6
7&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='l-Scalar-Plain'&gt;rvm&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;1.8.7&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;notifications&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;
  &lt;span class='l-Scalar-Plain'&gt;email&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;
    &lt;span class='p-Indicator'&gt;-&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;&amp;lt;your email address&amp;gt;&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;env&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;
  &lt;span class='p-Indicator'&gt;-&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;PUPPET_VERSION=2.6.14&lt;/span&gt;
  &lt;span class='p-Indicator'&gt;-&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;PUPPET_VERSION=2.7.11&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;This basically says, we want to use Ruby 1.8.7, run two sets of tests, one against Puppet 2.6.14 and the other against Puppet 2.7.11 and email the notifications through to your email address.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s one last thing we need to do and that is create a default &lt;code&gt;rake&lt;/code&gt; task that runs both &lt;code&gt;rake spec&lt;/code&gt; and &lt;code&gt;rake lint&lt;/code&gt;. To do that, add the following to the end of your &lt;code&gt;Rakefile&lt;/code&gt;.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:default&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:spec&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:lint&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;If you haven&amp;#8217;t already done so, commit and push all this up to GitHub.&lt;/p&gt;

&lt;p&gt;Point your browser &lt;a href='http://travis-ci.org'&gt;Travis CI&lt;/a&gt; and login with your GitHub account. In your profile page, turn on tests for your module&amp;#8217;s repository.&lt;/p&gt;

&lt;p&gt;&lt;img src='https://img.skitch.com/20120302-e2y2xk2cxb6mwnuhhynrjfp7m8.jpg' alt='Turn on tests' /&gt;&lt;/p&gt;

&lt;p&gt;And wait for them to run!&lt;/p&gt;

&lt;p&gt;&lt;img src='https://img.skitch.com/20120302-txxietenui82dsxyjubajnqt2e.jpg' alt='Win' /&gt;&lt;/p&gt;

&lt;h2 id='tldr'&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;Go check out my &lt;a href='https://github.com/rodjek/puppet-logrotate'&gt;logrotate module&lt;/a&gt; for a working example.&lt;/p&gt;</content>
    </entry>
    
    <entry>
        <title>Stop Writing Puppet Modules That Suck</title>
        <link href="http://bombasticmonkey.com/2011/12/27/stop-writing-puppet-modules-that-suck/"/>
        <updated>2011-12-27T00:00:00+11:00</updated>
        <id>http://bombasticmonkey.com/2011/12/27/stop-writing-puppet-modules-that-suck/</id>
        <content type="html">&lt;p&gt;Whenever I need to setup a new service on one of my hosts, the first thing I do is head to &lt;a href='http://forge.puppetlabs.com'&gt;the forge&lt;/a&gt; and &lt;a href='https://github.com'&gt;GitHub&lt;/a&gt; to try and find a decent Puppet module that already exists for it.&lt;/p&gt;

&lt;p&gt;I almost always leave in disappointment.&lt;/p&gt;

&lt;h2 id='puppet_modules_are_libraries'&gt;Puppet modules are libraries&lt;/h2&gt;

&lt;p&gt;Much like &lt;code&gt;string.h&lt;/code&gt; provides everything you need to manipulate strings in C, your Puppet modules should provide everything needed to manage a service out of the box. By that I mean, I want to pull down your module to enable the functionality I need in Puppet &lt;strong&gt;without modifying your module at all&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id='package_file_service'&gt;Package, File, Service&lt;/h3&gt;

&lt;p&gt;Regrettably, most of the modules out there don&amp;#8217;t deviate from the basic &lt;code&gt;package&lt;/code&gt;, &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;service&lt;/code&gt; model.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gr'&gt;class&lt;/span&gt; &lt;span class='kn'&gt;ntp&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='kn'&gt;package&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;installed&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;

  &lt;span class='kn'&gt;file&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;/etc/ntp.conf&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt;  &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;file&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;source&lt;/span&gt;  &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;puppet:///modules/ntp/etc/ntp.conf&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;owner&lt;/span&gt;   &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;group&lt;/span&gt;   &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;mode&lt;/span&gt;    &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;0444&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;require&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kn'&gt;Package&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
    &lt;span class='nc'&gt;notify&lt;/span&gt;  &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;

  &lt;span class='kn'&gt;service&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;running&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;enable&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;While this might be all you need in your homogeneous environment, it&amp;#8217;s unlikely that this will work in someone elses environment without modifications.&lt;/p&gt;

&lt;h3 id='package_file_service_facter'&gt;Package, File, Service, Facter&lt;/h3&gt;

&lt;p&gt;A common extension to this model is to add conditionals using Facter variables to cover a set of different use cases (CentOS vs Debian, etc).&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gr'&gt;class&lt;/span&gt; &lt;span class='kn'&gt;ntp&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;case&lt;/span&gt; &lt;span class='nv'&gt;$&lt;/span&gt;&lt;span class='gr'&gt;::&lt;/span&gt;&lt;span class='err'&gt;operatingsystem&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
    &lt;span class='kn'&gt;Debian:&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
      &lt;span class='nv'&gt;$packagename&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;openntp&amp;#39;&lt;/span&gt;&lt;span class='gr' /&gt;
      &lt;span class='nv'&gt;$servicename&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;openntp&amp;#39;&lt;/span&gt;&lt;span class='gr' /&gt;
    &lt;span class='gr'&gt;}&lt;/span&gt;
    &lt;span class='na'&gt;default&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
      &lt;span class='nv'&gt;$packagename&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr' /&gt;
      &lt;span class='nv'&gt;$servicename&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr' /&gt;
    &lt;span class='gr'&gt;}&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;

  &lt;span class='kn'&gt;package&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='nv'&gt;$packagename&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;installed&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;

  &lt;span class='kn'&gt;file&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;/etc/ntp.conf&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt;  &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;file&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;source&lt;/span&gt;  &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;puppet:///modules/ntp/etc/ntp.conf&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;owner&lt;/span&gt;   &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;group&lt;/span&gt;   &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;mode&lt;/span&gt;    &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;0444&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;require&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kn'&gt;Package&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;$packagename&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
    &lt;span class='s'&gt;notify&lt;/span&gt;  &lt;span class='s'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;$servicename&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='s'&gt;}&lt;/span&gt;

  &lt;span class='s'&gt;service&lt;/span&gt; &lt;span class='s'&gt;{&lt;/span&gt; &lt;span class='nv'&gt;$servicename&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;running&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='nc'&gt;enable&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;true&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
  &lt;span class='s'&gt;}&lt;/span&gt;
&lt;span class='s'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;While this is slightly more useful, it doesn&amp;#8217;t cover the possibility of wanting to override a value on a per-host basis.&lt;/p&gt;

&lt;h3 id='package_file_service_facter_and_global_variables_seriously'&gt;Package, File, Service, Facter and&amp;#8230; Global Variables? Seriously?&lt;/h3&gt;

&lt;p&gt;If you ask most people for the &amp;#8220;best practice&amp;#8221; method of passing parameters to their modules, they&amp;#8217;ll probably recommend using global variables - where you set a variable in your node definition or ENC and it magically gets picked up inside the various classes that you&amp;#8217;ve included.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gr'&gt;node&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;foo.example.com&amp;#39;&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='nv'&gt;$ntp_running&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;
  &lt;span class='nv'&gt;$monitor&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;
  &lt;span class='nv'&gt;$backup&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;
  &lt;span class='nv'&gt;$fml&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;

  &lt;span class='o'&gt;in&lt;/span&gt;&lt;span class='s'&gt;clude&lt;/span&gt; &lt;span class='s'&gt;ntp&lt;/span&gt;&lt;span class='gr' /&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;

&lt;span class='gr'&gt;class&lt;/span&gt; &lt;span class='kn'&gt;ntp&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;&lt;span class='c-Singleline' /&gt;
&lt;span class='c-Singleline'&gt;  # trimmed for brevity&lt;/span&gt;

  &lt;span class='kn'&gt;service&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='nv'&gt;$servicename&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;ensure&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='nv'&gt;$ntp_running&lt;/span&gt; &lt;span class='o'&gt;?&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
      &lt;span class='nc'&gt;true&lt;/span&gt;  &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;running&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
      &lt;span class='nc'&gt;false&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;stopped&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;,&lt;/span&gt;
    &lt;span class='gr'&gt;},&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Do we really need to go into why this is a bad idea? Puppet&amp;#8217;s variable scoping is confusing enough on it&amp;#8217;s own.&lt;/p&gt;

&lt;h2 id='so_what_should_you_be_doing'&gt;So what should you be doing&lt;/h2&gt;

&lt;p&gt;To put it simply, write your Puppet modules like you would write a library for your favorite programming language. Don&amp;#8217;t know a programming language? You&amp;#8217;re working towards the almighty &amp;#8220;Infrastructure as Code&amp;#8221; ideal. Stop making excuses like &amp;#8220;I don&amp;#8217;t know how to program so I didn&amp;#8217;t know any better&amp;#8221; and go and learn a language.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don&amp;#8217;t make your users edit your code to do the work they want.&lt;/li&gt;

&lt;li&gt;Don&amp;#8217;t rely on global variables to pass information to your modules.&lt;/li&gt;

&lt;li&gt;Make it easy for other people to add &lt;strong&gt;features&lt;/strong&gt; to your module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While it looks like the 3rd point contradicts the 1st point, it doesn&amp;#8217;t. There&amp;#8217;s a big difference between someone sending you a patch to your module to support a distro that it doesn&amp;#8217;t currently support and someone having to go and edit a template inside your module in order to change a config value that you didn&amp;#8217;t think anyone would need to change.&lt;/p&gt;

&lt;h2 id='an_example_of_a_good_module'&gt;An example of a good module&lt;/h2&gt;

&lt;p&gt;Take a look at the Puppet Labs &lt;a href='https://github.com/puppetlabs/puppetlabs-ntp/blob/master/manifests/init.pp'&gt;NTP module&lt;/a&gt;. It&amp;#8217;s not perfect, but it&amp;#8217;s pretty close.&lt;/p&gt;

&lt;p&gt;First off, it&amp;#8217;s using a parameterised class, so there&amp;#8217;s no global variables polluting the namespace while still allowing us to change the parameters used to configure NTP without editing the module itself. If you&amp;#8217;re using an older version of Puppet that doesn&amp;#8217;t support parameterised classes, a defined type will work just as well.&lt;/p&gt;

&lt;p&gt;Secondly, it has support for a good number of different distributions &lt;em&gt;but&lt;/em&gt; it has a default case that causes Puppet to abort and let the user know that this module isn&amp;#8217;t supported on their machine. This is much better than just blindly making changes to a system using values that may or may not work for it.&lt;/p&gt;

&lt;p&gt;For the folks using ENCs that don&amp;#8217;t support parameterised classes or defined types, you&amp;#8217;re still OK because you can just put this into your role definition like so&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt;1
2
3
4
5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gr'&gt;class&lt;/span&gt; &lt;span class='kn'&gt;mycompany::role::frontend&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='kn'&gt;class&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;servers&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;ntp.mycompany.com&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h3 id='how_this_module_could_be_made_even_better'&gt;How this module could be made even better&lt;/h3&gt;

&lt;p&gt;The addition of a generic &lt;code&gt;ntp::config&lt;/code&gt; type that used either Augeas or parsedfile on the back end to set arbitrary configuration values, i.e.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='kn'&gt;class&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
  &lt;span class='nc'&gt;servers&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;ntp.example.com&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;

&lt;span class='kn'&gt;ntp::config&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='s'&gt;&amp;#39;statsdir&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;value&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;/var/log/ntp&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;;&lt;/span&gt;
  &lt;span class='s'&gt;&amp;#39;statistics&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
    &lt;span class='nc'&gt;value&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;rawstats&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;;&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Also, replacing the &lt;code&gt;autoupdate&lt;/code&gt; parameter with a &lt;code&gt;version&lt;/code&gt; parameter so that it was possible to specify a particular package version, &amp;#8216;latest&amp;#8217;, &amp;#8216;installed&amp;#8217; etc. would be a great addition.&lt;/p&gt;

&lt;h2 id='thats_all_folks'&gt;That&amp;#8217;s all folks&lt;/h2&gt;

&lt;p&gt;How would you like to see modules written? What currently annoys you about the modules out there? Do you disagree with everything I&amp;#8217;ve written and want the last 10 minutes of your life back? Let me know.&lt;/p&gt;</content>
    </entry>
    
    <entry>
        <title>Auto-notify Resources From Your Puppet Types</title>
        <link href="http://bombasticmonkey.com/2011/11/13/autonotify-resources-from-your-puppet-types/"/>
        <updated>2011-11-13T00:00:00+11:00</updated>
        <id>http://bombasticmonkey.com/2011/11/13/autonotify-resources-from-your-puppet-types/</id>
        <content type="html">&lt;p&gt;So, I ran into a bit of a problem yesterday. I was working on a Puppet module for the &lt;a href='https://github.com/sonian/sensu/'&gt;Sensu&lt;/a&gt; monitoring framework, part of which involved writing a custom Puppet type to manage the configuration of service checks. Normally this is a trivial matter, however in this case, the checks must be configured on both the server &lt;strong&gt;and&lt;/strong&gt; the client and must therefore be able to notify either the server process or the client process or both to restart after a configuration change.&lt;/p&gt;

&lt;p&gt;Obviously, I couldn&amp;#8217;t just do this:&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt;1
2
3
4
5
6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='kn'&gt;sensu_check&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;mycheck&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;:&lt;/span&gt;
  &lt;span class='nc'&gt;notify&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='gr'&gt;[&lt;/span&gt;
    &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-client&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
    &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-server&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='gr'&gt;],&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;As if the host was only running the client process, this would result in an error during the Puppet runs.&lt;/p&gt;

&lt;p&gt;One option would be to use the following mess:&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='puppet'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='gr'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;defined&lt;/span&gt;&lt;span class='gr'&gt;(&lt;/span&gt;&lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-client&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;])&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='nf'&gt;defined&lt;/span&gt;&lt;span class='gr'&gt;(&lt;/span&gt;&lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-server&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;]))&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='kn'&gt;Sensu_check&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
    &lt;span class='nc'&gt;notify&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='gr'&gt;[&lt;/span&gt;
      &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-client&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
      &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-server&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
    &lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt; &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='nf'&gt;defined&lt;/span&gt;&lt;span class='gr'&gt;(&lt;/span&gt;&lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-client&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;])&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='kn'&gt;Sensu_check&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
    &lt;span class='nc'&gt;notify&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-client&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
  &lt;span class='kn'&gt;Sensu_check&lt;/span&gt; &lt;span class='gr'&gt;{&lt;/span&gt;
    &lt;span class='nc'&gt;notify&lt;/span&gt; &lt;span class='nn'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kn'&gt;Service&lt;/span&gt;&lt;span class='gr'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;sensu-server&amp;#39;&lt;/span&gt;&lt;span class='gr'&gt;],&lt;/span&gt;
  &lt;span class='gr'&gt;}&lt;/span&gt;
&lt;span class='gr'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Not the most elegant solution in the world. Ideally what I needed was something like the existing &lt;code&gt;autorequire&lt;/code&gt; functionality that creates &lt;code&gt;require&lt;/code&gt; relationships between your custom type and the named resources automatically but only if the resources exist in the node catalogue.&lt;/p&gt;

&lt;p&gt;Unfortunately, such functionality doesn&amp;#8217;t exist (yet), so I had to go some digging around in Puppet&amp;#8217;s codebase to implement it. The first thing to do was to establish a way to create arbitrary &lt;code&gt;notify&lt;/code&gt; relationships. This turned out to be easily done:&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6
7
8
9&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='no'&gt;Puppet&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Type&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;newtype&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:mytype&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;super&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:notify&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;
      &lt;span class='s2'&gt;&amp;quot;Service[myservice]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='s2'&gt;&amp;quot;Service[myotherservice]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Now, all I needed to do was make it so that it only notified the resources that actually existed in the node&amp;#8217;s manifest&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6
7
8
9&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='no'&gt;Puppet&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Type&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;newtype&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:mytype&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;super&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:notify&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;
      &lt;span class='s2'&gt;&amp;quot;Service[myservice]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='s2'&gt;&amp;quot;Service[myotherservice]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;select&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ref&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;catalog&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;resource&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;ref&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;The moral of this extremely drawn out story? Anything is possible in Puppet if you&amp;#8217;re willing to get your hands dirty. And this should really be a built in feature *hint*.&lt;/p&gt;</content>
    </entry>
    
    <entry>
        <title>Test Your Puppet Modules - Functions</title>
        <link href="http://bombasticmonkey.com/2011/11/04/test-your-puppet-modules-functions/"/>
        <updated>2011-11-04T00:00:00+11:00</updated>
        <id>http://bombasticmonkey.com/2011/11/04/test-your-puppet-modules-functions/</id>
        <content type="html">&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='irc'&gt;1
2
3
4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='cp'&gt;11:00 &lt;/span&gt;&lt;span class='nt'&gt;&amp;lt;hubot&amp;gt; &lt;/span&gt;[puppet/master] merge branch &amp;#39;mysql-module-refactor&amp;#39; - dave
&lt;span class='cp'&gt;11:00 &lt;/span&gt;&lt;span class='nt'&gt;&amp;lt;hubot&amp;gt; &lt;/span&gt;dave is deploying puppet/master to production
&lt;span class='cp'&gt;11:23 &lt;/span&gt;&lt;span class='nt'&gt;&amp;lt;nagios&amp;gt; &lt;/span&gt;PROBLEM - MySQL on dbmaster1.initech.com is CRITICAL
&lt;span class='cp'&gt;11:24 &lt;/span&gt;&lt;span class='nt'&gt;&amp;lt;dave&amp;gt; &lt;/span&gt;oh fuck
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Look familiar?&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;ve worked with configuration management systems for a while, a situation like this has probably cropped up and ruined your day. If you&amp;#8217;re lucky enough to have a homogeneous environment, then you might have a staging environment that you can test changes out on, but what if you don&amp;#8217;t? A slight mistake in that harmless change you&amp;#8217;re working on could stop a service on hundreds of machines or worse (purge the mysql-server package and all it&amp;#8217;s data *cough*).&lt;/p&gt;

&lt;h2 id='why_you_should_be_writing_unit_tests_for_your_puppet_modules'&gt;Why you should be writing unit tests for your Puppet modules&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prevent situations like the one above.&lt;/li&gt;

&lt;li&gt;Catch any problems moving between Puppet releases before it hits production.&lt;/li&gt;

&lt;li&gt;Now we can do this too&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src='http://imgs.xkcd.com/comics/compiling.png' alt='Obligatory XKCD' /&gt;&lt;/p&gt;

&lt;h2 id='getting_started'&gt;Getting started&lt;/h2&gt;

&lt;p&gt;In this article, we&amp;#8217;ll cover setting up your testing environment and cover how to write unit tests for your Puppet functions. I&amp;#8217;m going to make a few assumptions now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You store your Puppet manifests in git.&lt;/li&gt;

&lt;li&gt;You run a *nix machine as your workstation.&lt;/li&gt;

&lt;li&gt;You don&amp;#8217;t mind getting your hands dirty with a bit of simple Ruby.&lt;/li&gt;

&lt;li&gt;You have Ruby installed (1.8.7).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First of all, we&amp;#8217;re going to install &lt;a href='http://gembundler.com'&gt;Bundler&lt;/a&gt; to manage the dependencies our Puppet testing rig will have.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='console'&gt;1
2
3
4
5
6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gp'&gt;$&lt;/span&gt; gem install bundler --no-ri --no-rdoc
&lt;span class='go'&gt;Fetching: bundler-1.0.21.gem (100%)&lt;/span&gt;
&lt;span class='go'&gt;Successfully installed bundler-1.0.21&lt;/span&gt;
&lt;span class='go'&gt;1 gem installed&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt; bundle --version
&lt;span class='go'&gt;Bundler version 1.0.21&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Next we&amp;#8217;re going to create a &lt;code&gt;Gemfile&lt;/code&gt; in the top level of our Puppet repo with the list of gems we&amp;#8217;re going to need. Adjust the &lt;code&gt;puppet&lt;/code&gt; and &lt;code&gt;facter&lt;/code&gt; versions to match your environment.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='n'&gt;source&lt;/span&gt; &lt;span class='ss'&gt;:rubygems&lt;/span&gt;

&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;puppet&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;       &lt;span class='s1'&gt;&amp;#39;2.6.12&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;facter&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;       &lt;span class='s1'&gt;&amp;#39;1.6.0&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec-puppet&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;0.1.0&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;         &lt;span class='s1'&gt;&amp;#39;0.8.7&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Next we&amp;#8217;re going to add a couple of things to your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='text'&gt;1
2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;vendor/gems/
.bundle/
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Now we just need to tell bundler to install everything and commit our changes to the repository.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='console'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gp'&gt;$&lt;/span&gt; bundle install --path vendor/gems
&lt;span class='go'&gt;Fetching source index for http://rubygems.org/&lt;/span&gt;
&lt;span class='go'&gt;Installing rake (0.8.7)&lt;/span&gt;
&lt;span class='go'&gt;Installing diff-lcs (1.1.3)&lt;/span&gt;
&lt;span class='go'&gt;Installing facter (1.6.0)&lt;/span&gt;
&lt;span class='go'&gt;Installing puppet (2.6.12)&lt;/span&gt;
&lt;span class='go'&gt;Installing rspec-core (2.7.1)&lt;/span&gt;
&lt;span class='go'&gt;Installing rspec-expectations (2.7.0)&lt;/span&gt;
&lt;span class='go'&gt;Installing rspec-mocks (2.7.0)&lt;/span&gt;
&lt;span class='go'&gt;Installing rspec (2.7.0)&lt;/span&gt;
&lt;span class='go'&gt;Installing rspec-puppet (0.1.0)&lt;/span&gt;
&lt;span class='go'&gt;Using bundler (1.0.21)&lt;/span&gt;
&lt;span class='go'&gt;Your bundle is complete! It was installed into ./vendor/gems&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt; git add Gemfile
&lt;span class='gp'&gt;$&lt;/span&gt; git add Gemfile.lock
&lt;span class='gp'&gt;$&lt;/span&gt; git commit -a -m &lt;span class='s2'&gt;&amp;quot;Bundler setup for testing&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;OK, time to configure &lt;a href='https://www.relishapp.com/rspec'&gt;RSpec&lt;/a&gt;. Create a &lt;code&gt;spec&lt;/code&gt; directory in the root of your Puppet repository and create a file in the &lt;code&gt;spec&lt;/code&gt; folder called &lt;code&gt;spec_helper.rb&lt;/code&gt;. Adjust &lt;code&gt;c.module_path&lt;/code&gt; and &lt;code&gt;c.manifest_dir&lt;/code&gt; to point to your modules and manifests directories in your repository.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec-puppet&amp;#39;&lt;/span&gt;

&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;module_path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;modules&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;manifest_dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;manifests&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;The last thing we need to do is create a &lt;a href='http://rake.rubyforge.org'&gt;Rake&lt;/a&gt; task to run our tests. Create a &lt;code&gt;Rakefile&lt;/code&gt; in the root of your Puppet repository with the following.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3
4
5
6
7
8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec/core/rake_task&amp;#39;&lt;/span&gt;

&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Core&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;RakeTask&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:test&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pattern&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;modules/*/spec/*/*_spec.rb&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:default&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h2 id='testing_your_first_function'&gt;Testing your first function&lt;/h2&gt;

&lt;p&gt;For the sake of this example, let&amp;#8217;s create a simple module with a single function (that I&amp;#8217;m going to borrow from Puppet Lab&amp;#8217;s stdlib module).&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='console'&gt;1
2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gp'&gt;$&lt;/span&gt; mkdir -p modules/misc/lib/puppet/parser/functions
&lt;span class='gp'&gt;$&lt;/span&gt; mkdir -p modules/spec/functions
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Download &lt;a href='https://raw.github.com/puppetlabs/puppetlabs-stdlib/master/lib/puppet/parser/functions/bool2num.rb'&gt;this function&lt;/a&gt; and drop it in &lt;code&gt;modules/misc/lib/puppet/parser/functions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Basically this function should&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;return 0 if you pass it &amp;#8216;f&amp;#8217;, &amp;#8216;false&amp;#8217;, &amp;#8216;n&amp;#8217;, &amp;#8216;no&amp;#8217;, 0, &amp;#8221;, &amp;#8216;undef&amp;#8217; or &amp;#8216;undefined&amp;#8217;.&lt;/li&gt;

&lt;li&gt;return 1 if you pass it &amp;#8216;t&amp;#8217;, &amp;#8216;true&amp;#8217;, &amp;#8216;y&amp;#8217;, &amp;#8216;yes&amp;#8217; or 1.&lt;/li&gt;

&lt;li&gt;raise Puppet::ParseError if you pass it anything else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we continue, you should now go and read the &lt;a href='https://github.com/rodjek/rspec-puppet/blob/master/README.md'&gt;rspec-puppet README&lt;/a&gt;. The tests for this function should live in &lt;code&gt;modules/misc/spec/functions/bool2num_spec.rb&lt;/code&gt;.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec_helper&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bool2num&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;t&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;false&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;f&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;no&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;undef&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with_params&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_raise_error&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Puppet&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;ParseError&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Most of this should be pretty self explanatory, however there is a couple of important things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;spec_helper.rb&lt;/code&gt; required on line 1 is the &lt;code&gt;spec_helper.rb&lt;/code&gt; in the &lt;code&gt;spec&lt;/code&gt; directory at the root of your repository, not from the per-module &lt;code&gt;spec&lt;/code&gt; directory.&lt;/li&gt;

&lt;li&gt;The description on line 3 &lt;strong&gt;must&lt;/strong&gt; be a string and it &lt;strong&gt;must&lt;/strong&gt; be the name of the function that you are testing so that RSpec can set the subject correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now for the all important running of the tests.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='console'&gt;1
2
3
4
5
6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='gp'&gt;$&lt;/span&gt; bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rake
&lt;span class='go'&gt;/usr/bin/ruby -S rspec modules/misc/spec/functions/bool2num_spec.rb&lt;/span&gt;
&lt;span class='go'&gt;..............&lt;/span&gt;

&lt;span class='go'&gt;Finished in 2.61 seconds&lt;/span&gt;
&lt;span class='go'&gt;14 examples, 0 failures&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Huzzah! Now go and write tests for the rest of your functions. In the next article in this series, we&amp;#8217;ll cover how to test your Puppet manifests (&lt;code&gt;.pp&lt;/code&gt; files).&lt;/p&gt;</content>
    </entry>
    
    <entry>
        <title>Distributing Augeas lenses with Puppet's pluginsync</title>
        <link href="http://bombasticmonkey.com/2011/01/02/distributing-augeas-lenses-with-pluginsync/"/>
        <updated>2011-01-02T00:00:00+11:00</updated>
        <id>http://bombasticmonkey.com/2011/01/02/distributing-augeas-lenses-with-pluginsync/</id>
        <content type="html">&lt;p&gt;Sick of having to write &lt;code&gt;file&lt;/code&gt; resources to distribute the Augeas lenses that your module depends on? Why not use Puppet&amp;#8217;s pluginsync functionality to distribute them with the rest of your module?&lt;/p&gt;

&lt;p&gt;Under your module&amp;#8217;s &lt;code&gt;lib&lt;/code&gt; directory, create the following directory structure&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;modules/&amp;lt;module&amp;gt;/lib/
                     augeas/
                            lenses/&lt;/code&gt;&lt;/pre&gt;

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

&lt;pre&gt;&lt;code&gt;modules/&amp;lt;module&amp;gt;/manifests/
                 templates/
                 lib/
                     puppet/
                     facter/
                     augeas/
                            lenses/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Drop your Augeas lenses into this &lt;code&gt;lenses&lt;/code&gt; directory and Puppet will distribute them to all your clients automatically. Now we just need to tell Augeas where to find these lenses.&lt;/p&gt;

&lt;p&gt;The easiest way to go about this is to set a default &lt;code&gt;load_path&lt;/code&gt; value for Augeas type. To do that, add the following to your &lt;code&gt;site.pp&lt;/code&gt;.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;1
2
3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='no'&gt;Augeas&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='n'&gt;load_path&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;/usr/share/augeas/lenses:${settings::vardir}/augeas/lenses&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Note: Puppet will display an error for each lens during runs as it&amp;#8217;ll try to load them as Ruby files. It&amp;#8217;s noisy, but it&amp;#8217;s not a fatal error. I&amp;#8217;m hoping to find an easy way to prevent this.&lt;/p&gt;</content>
    </entry>
    
</feed>

