<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Tom Moertel's Weblog: A couple of tips for writing Puppet manifests</title>
    <link>http://blog.moertel.com/articles/2007/11/15/a-couple-of-tips-for-writing-puppet-manifests</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>A couple of tips for writing Puppet manifests</title>
      <description>&lt;p&gt;I recently started using &lt;a href="http://reductivelabs.com/trac/puppet"&gt;Puppet&lt;/a&gt;
to automate my server-build processes.  The basic idea behind Puppet
is that you create &amp;#8220;manifests&amp;#8221; that declare
a directed graph of &amp;#8220;resources&amp;#8221; that represents the desired state of
your machines.  Puppet-managed machines on your network then query a
master server to obtain the latest copy of the graph, which they then
reconcile with their current states to make whatever changes are
necessary to bring themselves up to date.&lt;/p&gt;


	&lt;p&gt;For the most part, everything works well.  I have encountered a couple
of snags when writing manifests, however, so I&amp;#8217;m going to explain them
here as reminder until I get the time to fix them in the Puppet code and send
patches upstream.&lt;/p&gt;


	&lt;p&gt;First, don&amp;#8217;t use hyphens in class names.  While hyphens are legal
in class names, they are not allowed in qualified variables, thus
variables defined within hyphen-named classes are inaccessible
from the outside world.&lt;/p&gt;


	&lt;p&gt;Second, and this one is both tricky and important, Puppet handles
prerequisites for definitions by silently passing those prerequisites on
to all of the resources within the definitions.  Definitions, in
effect, don&amp;#8217;t really have their own prerequisites, they just pass them on to
their children.  But &amp;#8211; and here&amp;#8217;s the problem &amp;#8211; if those child
resources declare their own prerequisites, those prerequisites will
&lt;em&gt;overwrite the passed-on prerequisites&lt;/em&gt;, effectively causing them to
be ignored.&lt;/p&gt;


	&lt;p&gt;This problem bit me hard when trying to create a definition for
installing Ruby Gems from a local cache of gems:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;define local_gem($gem) {
    $path = "/var/local/local-gems/$gem" 
    file { $path:
        ensure  =&amp;gt; present,
        source  =&amp;gt; "puppet://puppet/files/gems/$gem",
        require =&amp;gt; File["local-gems-dir"],
        owner   =&amp;gt; root,
        group   =&amp;gt; root,
        mode    =&amp;gt; 0664,
    }
    package { $title:
        ensure   =&amp;gt; installed,
        provider =&amp;gt; "gem",
        require  =&amp;gt; [ Package["rubygems"], File[$path] ],
        source   =&amp;gt; $path,
    }
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The intent was to be able to declare a local gem like so:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;local_gem { "sqlite3-ruby":
    gem     =&amp;gt; "sqlite3-ruby-1.2.1.gem",
    require =&amp;gt; Package["sqlite-devel"]
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Thus the &amp;#8220;sqlite3-ruby&amp;#8221; local gem has the single prerequisite of the
&amp;#8220;sqlite-devel&amp;#8221; package &amp;#8211; or at least that&amp;#8217;s what I expected.  What
happened on deployment was that the prerequisite was ignored because
when it was passed on to the inner file and package resources, those
resources had their own &lt;em&gt;require&lt;/em&gt; parameters, and those parameters
overwrote the passed-on prerequisite.&lt;/p&gt;


	&lt;p&gt;The work-around is somewhat hacky.  I augmented the definition with a do-nothing resource
that has no &lt;em&gt;require&lt;/em&gt; parameter of its own.  This
resource does nothing but capture the passed-on prerequisites.  Then I made
all of the other resources in the definition include the do-nothing
resource as one of their prerequisites.  Thus they are made to inherit the
passed-on prerequisites.&lt;/p&gt;


	&lt;p&gt;My final definition looks like this:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;define local_gem($gem) {

    # dummy exec to propagate requires from local_gem
    exec { $name: command =&amp;gt; "/bin/true" }

    $path = "/var/local/local-gems/$gem" 
    file { $path:
        ensure  =&amp;gt; present,
        source  =&amp;gt; "puppet://puppet/files/gems/$gem",
        require =&amp;gt; [ Exec[$name], File["local-gems-dir"] ],
        owner   =&amp;gt; root,
        group   =&amp;gt; root,
        mode    =&amp;gt; 0664,
    }
    package { $title:
        ensure   =&amp;gt; installed,
        provider =&amp;gt; "gem",
        require  =&amp;gt; [ Exec[$name], Package["rubygems"], File[$path] ],
        source   =&amp;gt; $path,
    }
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Notice how the file and package resource both require the dummy exec resource.
That&amp;#8217;s the trick that allows them to require the prerequisites passed on from
the local_gem definition.&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s not pretty, but it works.  &lt;a href="http://mail.madstop.com/pipermail/puppet-users/2007-March/001953.html"&gt;See this email on the puppet-users mailing list&lt;/a&gt; for more on the problem.&lt;/p&gt;</description>
      <pubDate>Thu, 15 Nov 2007 02:30:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:4fc6dc28-ae38-4d72-ac05-d9a65d6653f8</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2007/11/15/a-couple-of-tips-for-writing-puppet-manifests</link>
      <category>sysadmin</category>
      <category>rails</category>
      <category>puppet</category>
      <category>manifests</category>
      <category>gems</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/622</trackback:ping>
    </item>
  </channel>
</rss>
