<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>serialized.net &#187; Puppet</title>
	<atom:link href="http://serialized.net/category/tech/puppet/feed/" rel="self" type="application/rss+xml" />
	<link>http://serialized.net</link>
	<description>An ongoing expression of fascination burnout</description>
	<lastBuildDate>Tue, 15 Jun 2010 05:31:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Using custom functions in Puppet templates</title>
		<link>http://serialized.net/2010/01/using-custom-functions-in-puppet-templates/</link>
		<comments>http://serialized.net/2010/01/using-custom-functions-in-puppet-templates/#comments</comments>
		<pubDate>Sat, 02 Jan 2010 00:09:48 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Puppet]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=353</guid>
		<description><![CDATA[This eventually required a support ticket to figure out, so I&#8217;m documenting it here in case it&#8217;s useful to others. The problem: You have a puppet function you find useful. At this point custom functions are really easy to create and distribute via modules so there&#8217;s really no reason not to be using them. However, [...]]]></description>
			<content:encoded><![CDATA[<p>This eventually required a support ticket to figure out, so I&#8217;m documenting it here in case it&#8217;s useful to others.</p>
<p>The problem: You have a puppet function you find useful. At this point custom functions are <a href="http://reductivelabs.com/trac/puppet/wiki/PluginsInModules">really easy to create and distribute via modules</a> so there&#8217;s really no reason not to be using them. However, you want to use the result of a function in your templates.</p>
<p>A real-world example of this is sharing authentication information. (For example, crypted password hashes. Plain text, as always, would be evil no matter where it was living.)<br />
You really have 3 choices:</p>
<ul>
<li>Coding it right into your puppet code and checking that into your version control system</li>
<li>Not distributing that information with puppet</li>
<li>Using puppet to distribute the information, but store it outside of your puppet modules somehow.</li>
</ul>
<p>We chose the third way, and store this kind of information in YAML files outside the puppet modules tree.<br />
Anyway. So we want to access this info inside a template.</p>
<p>Assume you have a function called &#8216;get_extdata&#8217;.<br />
You normally call from your &#8216;.pp&#8217; manifests with something like</p>
<pre class="brush: ruby;">
$data = get_extdata('mymodule', 'path:to:data')
</pre>
<p>You can make a call like that from your template by doing this wonderful fragment of magic-wand-waving to get this function accessible:</p>
<p>In <b>mytemplate.erb</b>:</p>
<pre class="brush: plain;">
&lt;% Puppet::Parser::Functions.autoloader.loadall %&gt;
Now you can use the 'scope' variable to get at your function.
This is the equivalent of the call get_extdata('mymodule',  'path:to:data'):
&lt;%= scope.function_get_extdata('mymodule',  'path:to:data') %&gt;
</pre>
<p>This works like a charm, and means you don&#8217;t have to create a bunch of variables that you don&#8217;t really need before you load the template.</p>
<p>So if you were creating an .htpasswd style file with puppet, you could do</p>
<p>In your <b>init.pp</b></p>
<pre class="brush: ruby;">
$users = ['steve', 'paul', 'stu']
</pre>
<p><b>htpasswd.erb</b>:</p>
<pre class="brush: plain;">
&lt;% Puppet::Parser::Functions.autoloader.loadall %&gt;
&lt;% users.each do |user| -%&gt;
&lt;%= user %&gt;:&lt;%= scope.function_get_extdata('authmodule',  user) %&gt;
&lt;% end -%&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2010/01/using-custom-functions-in-puppet-templates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Puppet Module Patterns</title>
		<link>http://serialized.net/2009/07/puppet-module-patterns/</link>
		<comments>http://serialized.net/2009/07/puppet-module-patterns/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 16:53:04 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=273</guid>
		<description><![CDATA[I&#8217;ve been playing a lot with Puppet recently, and have been really focusing on getting a core set of modules in place. I found myself using a few module patterns over and over, so I thought I&#8217;d write them down and keep an updated list. /base and /site Our puppet modules are stored in 2 [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been playing a lot with Puppet recently, and have been really focusing on getting a core set of modules in place. I found myself using a few module patterns over and over, so I thought I&#8217;d write them down and keep an updated list.</p>
<h2><tt>/base</tt> and <tt>/site</tt></h2>
<p>Our puppet modules are stored in 2 directories: <tt>/base</tt> and <tt>/site</tt>. Here are the purposes:</p>
<h4><tt>/base</tt></h4>
<p>The best way to think of what goes in <tt>/base</tt> is:</p>
<blockquote><p>
I should be able to (and should!) post my <tt>/base</tt> tree to github or my blog, and it would be useful to other people.
</p></blockquote>
<p>Examples of things that might go into base are:</p>
<ul>
<li>virtuozzo</li>
<li>nginx</li>
<li>lighttpd</li>
<li>perlbal</li>
<li>debrepo</li>
<li>rpmrepo</li>
<li>openvpn</li>
<li>mysql</li>
<li>memcached</li>
</ul>
<p>Essentially, the environments that make daemons or applications useable.</p>
<h4><tt>/site</tt></h4>
<p>Site should be </p>
<blockquote><p>This should not be in any way useful (or safe to share) outside my company</p></blockquote>
<p>Good examples might be:</p>
<ul>
<li>mywebapp</li>
<li>mystatshud</li>
<li>mymonitoringsystem</li>
</ul>
<h2> Module Design </h2>
<p>Any functionality we want to do on a server can more or less be defined as:</p>
<blockquote><p>I want to set up some applications and an environment in a certain way, so that it does what I want</p></blockquote>
<h3> Sample application: <tt>mywebapp</tt></h3>
<p>So, for example, if we wanted to build an <tt>mywebapp</tt> module, we&#8217;d need certain things to make that work.<br />
Let&#8217;s assume that we need</p>
<ul>
<li>lighttpd installed and configured in a certain way</li>
<li>apache installed and configured in a certain way</li>
<li>some perl libraries installed</li>
<li>some cron jobs set up</li>
</ul>
<p>and so on.</p>
<h3>The obvious way</h3>
<p>The way I started out solving these sorts of problems turns out to not scale all that well.<br />
As I started a module (to solve a specific problem) I would just start listing out the puppet resources it would need.</p>
<p><b><tt>mywebapp/manifests/init.pp</tt></b></p>
<pre class="brush: plain;">
package { &amp;quot;lighttpd&amp;quot;: ensure =&amp;gt; installed }
service { &amp;quot;lighttpd&amp;quot;: ensure =&amp;gt; running, enabled =&amp;gt; true }
package { &amp;quot;lib-catalyst-rest-perl&amp;quot;: ensure =&amp;gt; installed }
package { &amp;quot;apache2&amp;quot;: ensure =&amp;gt; installed }
service { &amp;quot;apache2&amp;quot;: ....
</pre>
<p>and so on.</p>
<p>This breaks down in several ways. </p>
<h4>Things can only be defined once in puppet</h4>
<p>First, puppet only lets us define a resource once. So if we decide we want a new tool that needs the package <tt>lib-catalyst-rest-perl</tt> installed, or needs to use apache to host some stats HUD,<br />
we&#8217;d have a problem.</p>
<p><b><tt>statshud/manifests/init.pp</tt></b></p>
<pre class="brush: plain;">
...
package { &amp;quot;apache2&amp;quot;: ensure =&amp;gt; installed }
...
</pre>
<p>Puppet will freak.</p>
<p style="color: red">
Puppet::Parser::AST::Resource failed with error ArgumentError: Duplicate definition:<br />
Package[apache2] is already defined in file statshub/manifests/init.pp at line 1;<br />
cannot redefine at statshub/manifests/init.pp:2 on node mondrian
</p>
<h4>Things would get repeated a lot</h4>
<p>As a practical example, I refer you to the our lighttpd config.</p>
<p>It has an SSL config file, in which we find this little gem:</p>
<pre class="brush: plain;">
ssl.cipher-list = &amp;quot;DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA EDH-RSA-DES-CBC3-SHA
    AES256-SHA AES128-SHA DES-CBC3-SHA DES-CBC3-MD5 RC4-SHA RC4-MD5&amp;quot;
</pre>
<p>Neat! This implements the current best practices for what ciphers we should accept.</p>
<p>So having this in a <tt>base/</tt> module, which is used by everything that needs to spin up a lighttpd, means that all my stuff will be running in the safest way I know how to run it.</p>
<p>The alternative is not very pleasant: I&#8217;d need to copy and paste this block in every one of my modules that used lighttpd. I&#8217;d need to keep it updated in each one of those files if and when a cipher turns out to be insecure.</p>
<p>Similarly, I can consolidate a lot of intelligence like this:</p>
<ul>
<li>making sure log rotation is correct</li>
<li>making sure compression cache files are cleaned</li>
<li>making sure monit is configured to restart it if it dies</li>
<li>making sure file permissions are set up right</li>
<li>&#8230;.</li>
</ul>
<p>and, should we learn something New And Improved about any of these things, viola, all our useage of that module gets improved.</p>
<p>As an added benefit, this style of consolidation maps onto the way Puppet itself is going.<br />
Much like Perl has the CPAN, Puppet Modules will be getting some new features that allow them to be centralized. Soon we will be able to get &#8220;the puppet mysql module&#8221; rather than &#8220;some guy&#8217;s cloned module from a cloned module on github&#8221;.</p>
<h2>Module Patterns</h2>
<p>Because of the above threats and virtues, we want to push as much functionality as we can into base modules.</p>
<h3>Basic Design</h3>
<p><img src="http://serialized.net/wp-content/uploads/2009/07/base_site_modules.png" alt="Puppet Module Designs" title="Puppet Module Designs" width="284" height="194" class="alignnone size-full wp-image-279" /></p>
<p>So how this looks in practice in puppet-speak:</p>
<p><b><tt>mywebapp/manifests/init.pp</tt></b></p>
<pre class="brush: plain;">

// configure lighttpd module
include lighttpd

// configure apache module
include apache

// configure mysql module
include mysql

// set up crons
cron {
    command =&amp;gt; &amp;quot;....&amp;quot;
}
</pre>
<h3>Configuring other Classes</h3>
<p>Ok, great, I&#8217;ve shipped all that intelligence to another module. But how do I make it work the way I want it to?</p>
<p>Well, we want to configure other classes. There are 3 basic methods to configure a class:</p>
<ul>
<li>Create subclasses and only include one or some of them</li>
<li>Create &#8216;defines&#8217; that we use from inside classes</li>
<li>Set variables before you include it</li>
</ul>
<h4>Technique #1: Create subclasses and only include one or some of them</h4>
<p>I&#8217;ll keep this one simple:</p>
<p>A lot of the time there are several distinct modes you might want to use an app in. For example, there&#8217;s a big difference between</p>
<pre class="brush: plain;">
include mysql
</pre>
<p>and </p>
<pre class="brush: plain;">
include mysql::server
</pre>
<h4>Technique #2: Create &#8216;defines&#8217; that we use from inside classes</h4>
<p>This technique is the most commonly used one. Many of the application and daemon classes we want to configure have lots of &#8220;parts&#8221; that can be enabled. Some examples:</p>
<ul>
<li>monit config chunks</li>
<li>apache/lighttpd/nginx sites</li>
<li>subversion repos</li>
</ul>
<p>These are best handled with defines. A good full-strength version of this is in our monit config:</p>
<pre class="brush: plain;">
define conf ( $source = '', $content = '' ) {
    if $source != '' {
        file { &amp;quot;/etc/monit.d/$name&amp;quot;:
            notify =&amp;gt; Service[&amp;quot;monit&amp;quot;],
            source =&amp;gt; $source,
            mode =&amp;gt; 644,
            owner =&amp;gt; root,
            group =&amp;gt; root,
        }
    } else {
        file { &amp;quot;/etc/monit.d/$name&amp;quot;:
            notify =&amp;gt; Service[&amp;quot;monit&amp;quot;],
            content =&amp;gt; $content,
            mode =&amp;gt; 644,
            owner =&amp;gt; root,
            group =&amp;gt; root,
        }
    }
}
</pre>
<p>Notable features:</p>
<pre class="brush: plain;"> define conf ( $source = '', $content = '' ) </pre>
<p>This is useful because we can define a config file via either source or content.</p>
<p>I can call this from another module like:</p>
<pre class="brush: plain;">
include monit::common
monit::common::conf { &amp;quot;lighttpd-monit&amp;quot;: source =&amp;gt; &amp;quot;puppet:///lighttpd/lighthttpd-monitrc&amp;quot; }
</pre>
<p>or, if I wanted to template it:</p>
<pre class="brush: plain;">
include monit::common
monit::common::conf { &amp;quot;apache2&amp;quot;: content =&amp;gt; template(&amp;quot;apache2/apache2-monitrc.erb&amp;quot;) }
</pre>
<p>There&#8217;s no reason for us not to actually help people out and write common configs for them. This is easy with extra defines.</p>
<pre class="brush: plain;">
define simple_service ($pidname = $name) {
    file { &amp;quot;/etc/monit.d/$name&amp;quot;:
        notify =&amp;gt; Service[&amp;quot;monit&amp;quot;],
        content =&amp;gt; template(&amp;quot;monit/simple_service.erb&amp;quot;),
        mode =&amp;gt; 644,
        owner =&amp;gt; root,
        group =&amp;gt; root,
    }
}
</pre>
<pre class="brush: plain;">
include monit::common
monit::common::simple_service { &amp;quot;apache2&amp;quot;: pidname =&amp;gt; &amp;quot;apache2&amp;quot; }
</pre>
<h4>Technique #3: Set variables before you include the class</h4>
<p>A good example might be for the mywebapp, I only want apache2 to be listening to the <tt>lo</tt> loopback interface. This would be a dumb default behavior if someone just did</p>
<pre class="brush: plain;">
include apache2
</pre>
<p>I would probably want it listening on all the public interfaces, by default.</p>
<p>So I can specify configuration variables:</p>
<p>In </p>
<p><b><tt>site/mywebapp/manifests/init.pp</tt></b></p>
<pre class="brush: plain;">

// we only want to have apache2 listening on the loopback
$apache2_interface = &amp;quot;lo&amp;quot;

include apache2
</pre>
<p>Then, in </p>
<p><b><tt>base/apache2/manifests/init.pp</tt></b></p>
<pre class="brush: plain;">
// if someone set this variable before they included our class, use it
if($apache2_interface) {
    $interface = $apache2_interface
} else {
    // by default use '*' which means listen on all interfaces
    $interface = &amp;quot;*&amp;quot;
}
</pre>
<p>I like the idea of a consistent prefixing of configuration variables &#8212; for example, all config variables for module &#8216;foo&#8217; would be &#8216;$foo_&#8230;.&#8217;</p>
<p>Sometimes including a class doesn&#8217;t make sense unless we have some variable defined at all &#8212; there&#8217;s no reasonable default.<br />
Let&#8217;s say we&#8217;re configuring our local syslog &#8212; the server we syslog to. Without defining that, it doesn&#8217;t make sense to include the module.</p>
<p>We get this pretty much for free, actually. If you specify in your documentation:</p>
<blockquote><p>
You must specify a value for <tt>$syslog_server</tt> before including this module
</p></blockquote>
<p><b><tt>base/syslog-client/manifests/init.pp</tt></b></p>
<pre class="brush: plain;">
    file { &amp;quot;/etc/syslog.conf&amp;quot;:
        content =&amp;gt; template(syslog-client/syslog.conf.erb),
    }
</pre>
<p><b><tt>base/syslog-client/templates/syslog.conf.erb</tt></b></p>
<pre class="brush: plain;">
#
# Remote Logging
#

destination d_remote {
        tcp(&amp;quot;&amp;lt;%= syslog_server %&amp;gt;&amp;quot;, port(514));
};
</pre>
<p>So if someone tries to </p>
<pre class="brush: plain;">
include syslog-client
</pre>
<p>without defining that variable, puppet will error for you.</p>
<p style="color: red">
Failed to parse template syslog.conf.erb: Could not find value for &#8216;syslog_server&#8217;
</p>
<h2>Conclusions</h2>
<p>I&#8217;ll keep this page updated as more useful patterns emerge.</p>
<p>Thinking about decomposing modules by <tt>/base</tt> and <tt>/site</tt> is a powerful model already, as it helps develop new variants on ideas in a really rapid way with little to no copy and paste.</p>
<p>I&#8217;ve found that my <tt>site</tt> modules are often in the 4-7 lines long range, and I automatically get a lot of my hard-earned &#8220;best ways to do things&#8221; along for the ride.</p>
<p>And since I&#8217;m not the only one making things around here, everyone else saves time (and avoids some n00b mistakes) by being able to develop things more quickly as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/07/puppet-module-patterns/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Managing volatile files with puppet</title>
		<link>http://serialized.net/2009/04/managing-volatile-files-with-puppet/</link>
		<comments>http://serialized.net/2009/04/managing-volatile-files-with-puppet/#comments</comments>
		<pubDate>Wed, 15 Apr 2009 22:21:56 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=259</guid>
		<description><![CDATA[I&#8217;m managing Linux HA (heartbeat2) from Puppet and I had a problem. There is a file called &#8216;cib.xml&#8217; used by heartbeat which I needed to manage. For a variety of boring reasons, when you make changes to this file, you must have a number in it which is the highest the app has ever seen. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m managing <a href="linux-ha.org">Linux HA</a> (heartbeat2) from <a href="http://reductivelabs.com">Puppet</a> and I had a problem.<br />
There is a file called &#8216;cib.xml&#8217; used by heartbeat which I needed to manage. For a variety of boring reasons, when you make changes to this file, you must have a number in it which is the highest the<br />
app has ever seen.</p>
<p>To make things easy, I use the current timestamp for this, as I&#8217;ve never seen that number go backwards.</p>
<p>Ok, not the normal kind of thing you do with Puppet, but it can be done. We just need to know what time it is in the template.</p>
<p>Thankfully Puppet lets us define custom facts, so here we go&#8230;<br />
in the module&#8217;s plugins/facter directory, I created &#8216;epoch_time.rb&#8217;.</p>
<pre class="brush: ruby;">
Facter.add(&quot;epoch_time&quot;) do
        setcode do
                Time.now.to_i
        end
end
</pre>
<p>Then I could use that in templates/heartbeat/cib.xml I said</p>
<pre class="brush: xml;">
 &lt;cib admin_epoch=&quot;&lt;%= epoch_time %&gt;&quot;&gt;
</pre>
<p>Well, that works fine, except for one little issue.</p>
<p>When puppet manages a file it does this:</p>
<ol>
<li>Renders out the templates and takes an md5sum of that</li>
<li>Sends that to the client, which checks it&#8217;s own md5sum</li>
<li>If the sums are different, the client overwrites the file with the old one</li>
<li>The client then updates anything &#8216;subscribed&#8217; to that file (for example, restarting a service.)</li>
</ol>
<p>So since puppet runs about every 30 minutes, that&#8217;s not good. I don&#8217;t want to be reloading the CIB database every 30 minutes. That&#8217;s bad.</p>
<p>This is what we get:</p>
<pre>
debug: File[/etc/ha.d/ha.cf]/checksum: Initializing checksum hash
debug: File[/etc/ha.d/ha.cf]: Creating checksum {md5}18530322762561ce59f1d414340b4c43
debug: File[/etc/ha.d/cib.xml]/checksum: Initializing checksum hash
debug: File[/etc/ha.d/cib.xml]: Creating checksum {md5}a02f0ca8a3cce64d7913faa3268e530b
debug: File[/etc/ha.d/cib.xml]/content: Executing 'diff /etc/ha.d/cib.xml /tmp/puppet-diffing20090415-3486-ska653-0'
1c1
< <cib admin_epoch="1239318580">
---
>  <cib admin_epoch="1239815009">
debug: File[/etc/ha.d/cib.xml]: Changing content
debug: File[/etc/ha.d/cib.xml]: 1 change(s)
</cib></pre>
<p>So here was my solution, and as inelegant as it is, it pretty much works.</p>
<ol>
<li>Manage a file called &#8216;cib.xml-puppet&#8217; instead</li>
<li>In this file, set admin_epoch=&#8221;0&#8243;</li>
<li>When THAT file changes, do a chained action which sets the epoch time correctly, and writes out the actual cib.xml file</li>
<li>Run the command to reload the CIB database in that and only that case.</li>
</ol>
<p>So the puppet code to make this happen:</p>
<pre class="brush: ruby;">
        exec { &quot;add-epoch-cib-xml&quot;:
                command =&gt; &quot;sed 's/admin_epoch=\&quot;0\&quot;/admin_epoch=\&quot;${epoch_time}\&quot;/' /etc/ha.d/cib.xml-puppet &gt; /etc/ha.d/cib.xml &amp;&amp; cibadmin -R -x /etc/ha.d/cib.xml&quot;,
                path =&gt; [&quot;/bin&quot;, &quot;/usr/sbin/&quot;],
                subscribe =&gt; File[&quot;/etc/ha.d/cib.xml-puppet&quot;],
                refreshonly =&gt; true,
        }
        file { &quot;/etc/ha.d/cib.xml-puppet&quot; :
                type =&gt; 'file',
                ensure =&gt; 'present',
                content =&gt; template(&quot;ha_nfsroot/heartbeat/cib.xml&quot;);
        }
</pre>
<p>So I still get to use my very first custom fact (it gets interpolated into the &#8216;sed&#8217; command) but I also don&#8217;t run that terrifying update every time.</p>
<p>If any puppetmasters or Linux HA gurus want to tell me exactly how I&#8217;m doing it wrong I would love to hear that. This certianly doesn&#8217;t feel &#8220;elegant.&#8221;<br />
However, since it feels &#8220;functional&#8221;, functionality trumps elegance for today.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/04/managing-volatile-files-with-puppet/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Editing puppet files with vim: now with &#8216;quickfix&#8217; support.</title>
		<link>http://serialized.net/2009/03/editing-puppet-files-with-vim-now-with-quickfix-support/</link>
		<comments>http://serialized.net/2009/03/editing-puppet-files-with-vim-now-with-quickfix-support/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 20:13:13 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=245</guid>
		<description><![CDATA[So the puppet guys provide some notes and files on running puppet with vim. One thing I didn&#8217;t see anything that provided a &#8216;compiler&#8217;, so I could edit a &#8216;.pp&#8217; file, then do :make to check for errors. (Check out :help make in your vim for more information about the quickfix tool.) This little tar [...]]]></description>
			<content:encoded><![CDATA[<p>So the puppet guys provide some notes and files on running puppet with vim.<br />
One thing I didn&#8217;t see anything that provided a &#8216;compiler&#8217;, so I could edit a &#8216;.pp&#8217; file, then do :make to check for errors. (Check out <code>:help make</code> in your vim for more information about the quickfix tool.)</p>
<p>This little tar contains all the files you need to make this work. Careful with it, as I have provided a minimalist .vimrc so don&#8217;t unpack this in your home directory.</p>
<p> <a href="http://serialized.net/wp-content/uploads/2009/03/puppet-vimtar.gz" title="puppet_vim.tar.gz">puppet_vim.tar.gz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/03/editing-puppet-files-with-vim-now-with-quickfix-support/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
