<?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>Coherent Ramblings</title>
	<atom:link href="http://plathrop.tertiusfamily.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://plathrop.tertiusfamily.net/blog</link>
	<description>My thoughts on everything from Operations through Parenting and beyond.</description>
	<lastBuildDate>Thu, 03 Jun 2010 17:12:31 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Required Watching</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/06/03/required-watching/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/06/03/required-watching/#comments</comments>
		<pubDate>Thu, 03 Jun 2010 17:12:31 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2010/06/03/required-watching/</guid>
		<description><![CDATA[This should be required watching for all managers and entrepreneurs.


]]></description>
			<content:encoded><![CDATA[<p>This should be required watching for all managers and entrepreneurs.</p>
<hr />
<object width="640" height="385"><param name="movie" value="http://www.youtube-nocookie.com/v/u6XAPnuFjJc&#038;hl=en_US&#038;fs=1&#038;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube-nocookie.com/v/u6XAPnuFjJc&#038;hl=en_US&#038;fs=1&#038;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/06/03/required-watching/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spiritual Safety Tip</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/24/spiritual-safety-tip/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/24/spiritual-safety-tip/#comments</comments>
		<pubDate>Mon, 24 May 2010 18:24:15 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Around The Web]]></category>
		<category><![CDATA[humor]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=65</guid>
		<description><![CDATA[
]]></description>
			<content:encoded><![CDATA[<p><img src="http://i8.photobucket.com/albums/a46/scguru9697/1135133596688.jpg" alt="Spiritual Safety Tip" /></p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/24/spiritual-safety-tip/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>clusto do-my-dishes</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/24/clusto-do-my-dishes/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/24/clusto-do-my-dishes/#comments</comments>
		<pubDate>Mon, 24 May 2010 17:50:27 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Clusto]]></category>
		<category><![CDATA[Digg]]></category>
		<category><![CDATA[Systems Engineering]]></category>
		<category><![CDATA[clusto]]></category>
		<category><![CDATA[humor]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=60</guid>
		<description><![CDATA[While discussing the proper class hierarchy for adding our Netscalers to Clusto, we decided that Appliance was the proper name for the base class.
This of course led to the idea of coding up a driver for a dishwasher. clusto do-my-dishes FTW!
]]></description>
			<content:encoded><![CDATA[<p>While discussing the proper class hierarchy for adding our <a href="http://www.citrix.com/english/ps2/products/product.asp?contentid=21679">Netscaler</a>s to <a href="http://clusto.org/">Clusto</a>, we decided that <code>Appliance</code> was the proper name for the base class.</p>
<p>This of course led to the idea of coding up a driver for a dishwasher. <code>clusto do-my-dishes</code> FTW!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/24/clusto-do-my-dishes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Empire Parody</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/19/empire-parody/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/19/empire-parody/#comments</comments>
		<pubDate>Wed, 19 May 2010 22:31:22 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Around The Web]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2010/05/19/empire-parody/</guid>
		<description><![CDATA[
]]></description>
			<content:encoded><![CDATA[<p><object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/KmTpOQrqoO0&#038;border=1&#038;color1=0x3a3a3a&#038;color2=0x999999&#038;hl=en_US&#038;feature=player_embedded&#038;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/KmTpOQrqoO0&#038;border=1&#038;color1=0x3a3a3a&#038;color2=0x999999&#038;hl=en_US&#038;feature=player_embedded&#038;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="640" height="385"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/19/empire-parody/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Weirdest Mail Ever</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/18/weirdest-mail-ever/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/18/weirdest-mail-ever/#comments</comments>
		<pubDate>Tue, 18 May 2010 18:10:27 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[weird]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=51</guid>
		<description><![CDATA[
UPDATE #2: Mystery solved! They are replacement stickies for our rain chimes, ordered by my lovely partner!
UPDATE: I just realized the envelope was folded in thirds; perhaps it was mailed inside another envelope to Seattle?


This morning I received the weirdest piece of (snail) mail I&#8217;ve ever seen. The envelope has what appear to be St. [...]]]></description>
			<content:encoded><![CDATA[<ul>
<li><strong>UPDATE #2</strong>: Mystery solved! They are replacement stickies for our <a href="http://www.amazon.com/executive-nature-chimes-light-cherry/dp/B000FADLK6">rain chimes</a>, ordered by my lovely partner!</li>
<li><strong>UPDATE</strong>: I just realized the envelope was folded in thirds; perhaps it was mailed inside another envelope to Seattle?</li>
</ul>
<hr/>
This morning I received the weirdest piece of (snail) mail I&#8217;ve ever seen. The envelope has what appear to be St. Jude style address labels with my info printed on them (I use these labels myself all the time). Both the &#8220;To&#8221; and return address labels have my name and address on them. The envelope is postmarked Seattle, and has a Forever stamp on it. Here&#8217;s the envelope:</p>
						<div class="flickr-gallery image none"><a href="http://www.flickr.com/photos/44578466@N00/4618790487"><img class="flickr medium" title="Envelope" alt="Envelope" src="http://farm4.static.flickr.com/3432/4618790487_1dd367d100.jpg" /></a></div>
					
<p>Inside was nothing but six machine-cut blank white sticky labels on a yellow backing:</p>
						<div class="flickr-gallery image none"><a href="http://www.flickr.com/photos/44578466@N00/4618790785"><img class="flickr medium" title="Contents" alt="Contents" src="http://farm5.static.flickr.com/4035/4618790785_ed5d2b4dc7.jpg" /></a></div>
					
<p>Does anybody have any clue what this is all about?</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/18/weirdest-mail-ever/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Git Hooks: Branch ACLs and more.</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/11/git-hooks-branch-acls-and-more/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/11/git-hooks-branch-acls-and-more/#comments</comments>
		<pubDate>Tue, 11 May 2010 20:47:45 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Git]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[automation]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=45</guid>
		<description><![CDATA[Recently at Digg, we wanted to open up the git repository containing our Puppet manifests so that our developers could work with it. However, we wanted to maintain some control over which branches they could push to, in order to prevent accidental commits to the production manifests. In addition to this fine-grained authorization, we wanted [...]]]></description>
			<content:encoded><![CDATA[<p>Recently at Digg, we wanted to open up the git repository containing our Puppet manifests so that our developers could work with it. However, we wanted to maintain some control over which branches they could push to, in order to prevent accidental commits to the production manifests. In addition to this fine-grained authorization, we wanted the ability to force their development branch to track ours; we always want them working from the latest code we&#8217;ve committed.</p>
<p>There are some heavier-weight options we could use to accomplish this sort of thing, but we aren&#8217;t yet ready to move to a more complicated tool chain for this sort of thing. I was pretty sure we could accomplish this with <a href="http://www.kernel.org/pub/software/scm/git/docs/githooks.html">git hooks</a>. I was right:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/env python</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">re</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">sys</span> <span style="color: #ff7700;font-weight:bold;">import</span> argv, exit
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">subprocess</span> <span style="color: #ff7700;font-weight:bold;">import</span> Popen, PIPE
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> blank<span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>:
    regex = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'^<span style="color: #000099; font-weight: bold;">\s</span>*$'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> regex.<span style="color: black;">search</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> uncomment<span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> line.<span style="color: black;">partition</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'#'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> cleanup<span style="color: black;">&#40;</span>lines<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># Remove full-line comments.</span>
    lines = <span style="color: black;">&#91;</span>line <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> line.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'#'</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    <span style="color: #808080; font-style: italic;"># Remove blank lines.</span>
    lines = <span style="color: black;">&#91;</span>line <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> blank<span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    <span style="color: #808080; font-style: italic;"># Remove comments from line ends.</span>
    lines = <span style="color: black;">&#91;</span>uncomment<span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines<span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> lines
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> parse_acl<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        fh = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'acl'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">IOError</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Could not open ACL file. Exiting.&quot;</span>
        exit<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Read and close ACL file.</span>
    lines = fh.<span style="color: black;">readlines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    fh.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    lines = cleanup<span style="color: black;">&#40;</span>lines<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># The ACL lines are whitespace separated. The first field is the</span>
    <span style="color: #808080; font-style: italic;"># username, the second field is a regex describing the refs the</span>
    <span style="color: #808080; font-style: italic;"># user is allowed to update.</span>
    access = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines:
        record = line.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        r_user = record<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        r_ref = record<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> r_user <span style="color: #ff7700;font-weight:bold;">in</span> access:
            access<span style="color: black;">&#91;</span>r_user<span style="color: black;">&#93;</span> = access<span style="color: black;">&#91;</span>r_user<span style="color: black;">&#93;</span> + <span style="color: black;">&#91;</span>r_ref<span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            access<span style="color: black;">&#91;</span>r_user<span style="color: black;">&#93;</span> = <span style="color: black;">&#91;</span>r_ref<span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> access
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> authorize<span style="color: black;">&#40;</span>refname=<span style="color: #483d8b;">''</span>, <span style="color: #dc143c;">user</span>=<span style="color: #483d8b;">''</span>, access=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">user</span> <span style="color: #ff7700;font-weight:bold;">in</span> access:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
    allowed_refs = access<span style="color: black;">&#91;</span><span style="color: #dc143c;">user</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> a_ref <span style="color: #ff7700;font-weight:bold;">in</span> allowed_refs:
        regex = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;^%s$&quot;</span> <span style="color: #66cc66;">%</span> a_ref<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> regex.<span style="color: black;">search</span><span style="color: black;">&#40;</span>refname<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> parse_git_config<span style="color: black;">&#40;</span>config_entry<span style="color: black;">&#41;</span>:
    output = Popen<span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;git&quot;</span>, <span style="color: #483d8b;">&quot;config&quot;</span>, <span style="color: #483d8b;">&quot;--bool&quot;</span>, config_entry<span style="color: black;">&#93;</span>, stdout=PIPE<span style="color: black;">&#41;</span>.<span style="color: black;">communicate</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> output.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> == <span style="color: #483d8b;">'true'</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> parse_branch_tracking<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        fh = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'branch_tracking'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">IOError</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Could not open branch tracking file. Exiting.&quot;</span>
        exit<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Read and close branch tracking file.</span>
    lines = fh.<span style="color: black;">readlines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    fh.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    lines = cleanup<span style="color: black;">&#40;</span>lines<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Branch tracking lines are whitespace separated. The first field</span>
    <span style="color: #808080; font-style: italic;"># is the branch that must track the branch specified in the second</span>
    <span style="color: #808080; font-style: italic;"># field.</span>
    tracking = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines:
        record = line.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        r_branch = record<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        r_track = record<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
        tracking<span style="color: black;">&#91;</span>r_branch<span style="color: black;">&#93;</span> = r_track
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> tracking
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> get_tracked_branch<span style="color: black;">&#40;</span>refname<span style="color: black;">&#41;</span>:
    track = parse_branch_tracking<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> track<span style="color: black;">&#91;</span>refname<span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">KeyError</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">None</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> get_missing_refs<span style="color: black;">&#40;</span>ref, base_ref<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># git rev-list gives us the commits reachable from base_ref that</span>
    <span style="color: #808080; font-style: italic;"># are NOT reachable from ref.</span>
    output = Popen<span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;git&quot;</span>, <span style="color: #483d8b;">&quot;rev-list&quot;</span>, <span style="color: #483d8b;">'%s..%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>ref, base_ref<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>, stdout=PIPE<span style="color: black;">&#41;</span>.<span style="color: black;">communicate</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> output.<span style="color: black;">splitlines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># The args are passed in by git.</span>
    refname = argv<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
    old_rev = argv<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>
    new_rev = argv<span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>
    <span style="color: #dc143c;">user</span> = <span style="color: #dc143c;">os</span>.<span style="color: black;">environ</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'USER'</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Check if 'push acl' is enabled.</span>
    push_acl = parse_git_config<span style="color: black;">&#40;</span><span style="color: #483d8b;">'hooks.pushacl'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> push_acl:
        <span style="color: #808080; font-style: italic;"># Check the user's authorization to update these git refs.</span>
        access = parse_acl<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> authorize<span style="color: black;">&#40;</span>refname=refname, <span style="color: #dc143c;">user</span>=<span style="color: #dc143c;">user</span>, access=access<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Could not update %s, permission denied by ACL.&quot;</span> <span style="color: #66cc66;">%</span> refname
            exit<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Check if 'forced branch tracking' is enabled.</span>
    force_tracking = parse_git_config<span style="color: black;">&#40;</span><span style="color: #483d8b;">'hooks.forcebranchtracking'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> force_tracking:
        tbranch = get_tracked_branch<span style="color: black;">&#40;</span>refname<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> tbranch:
            <span style="color: #808080; font-style: italic;"># Get the refs that are reachable from our tracked branch</span>
            <span style="color: #808080; font-style: italic;"># but NOT reachable from our new revision.</span>
            missed_refs = get_missing_refs<span style="color: black;">&#40;</span>new_rev, tbranch<span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>missed_refs<span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span>:
                <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Could not update %s, you need to merge %s.&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>refname, tbranch<span style="color: black;">&#41;</span>
                exit<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>This implements two git config options:<br />
<code><br />
git config --bool hooks.pushacl<br />
git config --bool hooks.forcebranchtracking<br />
</code><br />
They are both meant to be set on a bare git repository (<code>git init --bare</code>). The first option causes the update hook to look for a file called &#8216;acl&#8217; in the root of the bare repo. Here is the ACL file I&#8217;m using for our puppet configs right now:<br />
<code><br />
plathrop        refs/.*/.*<br />
ron             refs/.*/.*<br />
synack          refs/.*/.*<br />
wfrancis        refs/.*/.*<br />
kad             refs/.*/.*<br />
mike            refs/.*/.*<br />
rcoli           refs/heads/(development|(rcoli/.*)){1}<br />
goffinet        refs/heads/experimental<br />
rich            refs/heads/experimental<br />
kelvin          refs/heads/experimental<br />
</code><br />
The entries are regular expressions matching git &#8220;refs&#8221;. So, I have full access, rcoli can push to the development branch or any branch starting with &#8220;rcoli/&#8221; (but cannot create tags), and goffinet and his fellow developers can push to the experimental branch (but cannot create tags).</p>
<p>The second option is probably badly named. It looks for a file called branch_tracking in the root of the bare repo. That file looks like this:<br />
<code><br />
refs/heads/experimental    refs/heads/development<br />
</code><br />
When hooks.forcebranchtracking is set, the hook will enforce that the branch on the left contain all the commits from the branch on the right before it will accept any updates. Essentially this forces the experimental branch to track the development branch, and requires<br />
regular runs of <code>git pull</code> to stay in sync.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/11/git-hooks-branch-acls-and-more/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Good for a laugh</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/07/good-for-a-laugh/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/07/good-for-a-laugh/#comments</comments>
		<pubDate>Fri, 07 May 2010 21:06:23 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Around The Web]]></category>
		<category><![CDATA[humor]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=43</guid>
		<description><![CDATA[This totally cracks me up.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html">This totally cracks me up</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/07/good-for-a-laugh/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Debian Packaging Puppet Manifests</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/05/03/debian-packaging-puppet-manifests/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/05/03/debian-packaging-puppet-manifests/#comments</comments>
		<pubDate>Tue, 04 May 2010 00:57:55 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[packaging]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=39</guid>
		<description><![CDATA[With the (new?) ability for Puppet to have multiple module_dirs, the idea occurred to me: why not package a module as a Debian package?
The packaging process was relatively easy. Check out the results on GitHub.
Enjoy!
]]></description>
			<content:encoded><![CDATA[<p>With the (new?) ability for Puppet to have multiple module_dirs, the idea occurred to me: why not package a module as a Debian package?</p>
<p>The packaging process was relatively easy. Check out the results <a href="http://github.com/plathrop/puppet-module-supervisor/tree/master/debian/">on GitHub</a>.</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/05/03/debian-packaging-puppet-manifests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bottle Feeding</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/04/26/bottle-feeding/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/04/26/bottle-feeding/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 01:42:46 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Daddy Adventures]]></category>
		<category><![CDATA[fatherhood]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=37</guid>
		<description><![CDATA[My son is a breastfed baby.
Never has that been more clear to me than the last couple weeks. Aly has started work again, which means Connor needs to learn to take milk from a bottle, so that I can feed him while mom is seeing patients. We&#8217;ve got plenty of milk stored up from Connor&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>My son is a breastfed baby.</p>
<p>Never has that been more clear to me than the last couple weeks. Aly has started <a href="http://laughingbuddhaacupuncture.net/">work</a> again, which means Connor needs to learn to take milk from a bottle, so that I can feed him while mom is seeing patients. We&#8217;ve got plenty of milk stored up from Connor&#8217;s NICU days, and he&#8217;s fed from a bottle before; in fact, they wouldn&#8217;t release him from the NICU until he could take all of his feedings from a bottle. My feelings on this policy aside, the hope was that his familiarity with it would make things easier for us.</p>
<p>Maybe it would have, if we had kept it up. But in the four months since he&#8217;s been home from the hospital, he hasn&#8217;t touched a bottle (or a pacifier even, except to spit it out). So last week when we started trying bottle feeding, Connor let me know in no uncertain terms: &#8220;Boobs only, dad!&#8221;</p>
<p>This has been one of the singularly most frustrating things I&#8217;ve encountered so far. Every session ends with a cranky baby covered in milk and a frazzled dad who feels like a complete failure. I know he knows what to do, he makes all the right motions and signals like he does when breastfeeding, but he&#8217;s not interested in actually <em>sucking</em> on the damn bottle. Tonight we tried again, and I came away defeated, with a pounding headache to boot (from the screaming).</p>
<p>If at first you don&#8217;t succeed&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/04/26/bottle-feeding/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Blog Changes</title>
		<link>http://plathrop.tertiusfamily.net/blog/2010/04/24/blog-changes/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2010/04/24/blog-changes/#comments</comments>
		<pubDate>Sat, 24 Apr 2010 19:20:17 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Meta]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=34</guid>
		<description><![CDATA[I&#8217;ve decided to alter the direction of this blog (not that it had a lot of direction to begin with).
Thing is, I kept meaning to write more, and life kept getting in the way. After thinking about it more, it seems that the solution is to not restrict myself to just writing about work, and [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve decided to alter the direction of this blog (not that it had a lot of direction to begin with).</p>
<p>Thing is, I kept meaning to write more, and life kept getting in the way. After thinking about it more, it seems that the solution is to not restrict myself to just writing about work, and allow myself to write about life as it happens.</p>
<p>Let&#8217;s give it a go&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2010/04/24/blog-changes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Broken Permalinks</title>
		<link>http://plathrop.tertiusfamily.net/blog/2009/01/29/broken-permalinks/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2009/01/29/broken-permalinks/#comments</comments>
		<pubDate>Fri, 30 Jan 2009 05:43:24 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Meta]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2009/01/29/broken-permalinks/</guid>
		<description><![CDATA[I just migrated my blog to a new server. I&#8217;m using lighttpd instead of apache, and I still haven&#8217;t figured out how to get my permalinks working correctly. Be patient with me!
(UPDATE): Permalinks finally fixed.
]]></description>
			<content:encoded><![CDATA[<p>I just migrated my blog to a new server. I&#8217;m using lighttpd instead of apache, and I still haven&#8217;t figured out how to get my permalinks working correctly. Be patient with me!</p>
<p>(UPDATE): Permalinks finally fixed.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2009/01/29/broken-permalinks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Naming Is A Hard Problem</title>
		<link>http://plathrop.tertiusfamily.net/blog/2009/01/20/naming-is-a-hard-problem/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2009/01/20/naming-is-a-hard-problem/#comments</comments>
		<pubDate>Tue, 20 Jan 2009 22:27:06 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Digg]]></category>
		<category><![CDATA[Philosophy]]></category>
		<category><![CDATA[naming philosophy puppet]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=31</guid>
		<description><![CDATA[What should you name your hosts?

I used to believe this was an easy problem. Pick a theme, like <a href="http://en.wikipedia.org/wiki/List_of_Star_Wars_characters">Star Wars Characters</a> or <a href="http://en.wikipedia.org/wiki/List_of_constellations">Constellations</a>, and then name machines based on that theme: "chewbacca.tertiusfamily.net" for example. Use DNS CNAMEs to point your "functional" names (webapp1, or db1 or whatever) at the theme name and you're off. Then I started working for <a href="http://digg.com">Digg</a>.]]></description>
			<content:encoded><![CDATA[<p>What should you name your hosts?</p>
<p>I used to believe this was an easy problem. Pick a theme, like <a href="http://en.wikipedia.org/wiki/List_of_Star_Wars_characters">Star Wars Characters</a> or <a href="http://en.wikipedia.org/wiki/List_of_constellations">Constellations</a>, and then name machines based on that theme: &#8220;chewbacca.tertiusfamily.net&#8221; for example. Use DNS CNAMEs to point your &#8220;functional&#8221; names (webapp1, or db1 or whatever) at the theme name and you&#8217;re off. Then I started working for <a href="http://digg.com">Digg</a>.</p>
<p><a href="http://blog.digg.com/?p=168">Digg&#8217;s infrastructure</a> is massively horizontally-scaled. That means clusters, which means *lots* of individual nodes. No matter what &#8220;theme&#8221; you choose, you run out of names awfully fast. Not only that, but when you start building out a service-oriented architecture, you stop wanting to deal with individual nodes and start thinking in terms of clusters which provide services instead.</p>
<p>At the Puppet training I went to in Portland, we discussed the issue and I found out that there are two camps (well, as many camps as there are sysadmins, but it can be broken down into two camps). On the one side are those who want to put meta-data into their hostnames. On the other are those who think that hostnames should be as arbitrary as possible. There are strong arguments either way.</p>
<p>I <em>think</em> I come down firmly on the side of arbitrary hostnames. I&#8217;m coming to believe that, since IP addresses are necessarily unique, they should be the only unique identifier for an individual node. But I can&#8217;t come up with a solid argument as to why that is the best way.</p>
<p>What do you do about naming?</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2009/01/20/naming-is-a-hard-problem/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Bad Blogger, no biscuit!</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/10/10/bad-blogger-no-biscuit/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/10/10/bad-blogger-no-biscuit/#comments</comments>
		<pubDate>Fri, 10 Oct 2008 19:29:14 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Meta]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=30</guid>
		<description><![CDATA[Yeah, I haven&#8217;t been updating like I should. But, I&#8217;m apparently pretty high on the list for Google results when searching for Puppet recipes, so maybe I should fix my theme and start updating again&#8230;
]]></description>
			<content:encoded><![CDATA[<p>Yeah, I haven&#8217;t been updating like I should. But, I&#8217;m apparently pretty high on the list for Google results when searching for Puppet recipes, so maybe I should fix my theme and start updating again&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/10/10/bad-blogger-no-biscuit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting python-ldap installed on OS X</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/07/07/getting-python-ldap-installed-on-os-x/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/07/07/getting-python-ldap-installed-on-os-x/#comments</comments>
		<pubDate>Mon, 07 Jul 2008 23:13:25 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Quick Tips]]></category>
		<category><![CDATA[ldap]]></category>
		<category><![CDATA[osx]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=29</guid>
		<description><![CDATA[If you want to use Fink, and build a bunch of crap you don&#8217;t need, follow these instructions.
If you are like me and prefer to use the libraries that are already installed, just do this:

If you haven&#8217;t already, install XCode.
Download python-ldap
 and extract it.
Edit setup.cfg. The relevant lines are below: 

library_dirs = /Developer/SDKs/MacOSX10.5.sdk/usr/lib/
include_dirs = /Developer/SDKs/MacOSX10.5.sdk/usr/include/ [...]]]></description>
			<content:encoded><![CDATA[<p>If you want to use Fink, and build a bunch of crap you don&#8217;t need, follow <a href="http://vanrees.org/weblog/getting-python-ldap-running-on-osx-fink">these instructions</a>.</p>
<p>If you are like me and prefer to use the libraries that are already installed, just do this:</p>
<ol>
<li>If you haven&#8217;t already, install XCode.
<li><a href="http://python-ldap.sourceforge.net/download.shtml">Download</a> python-ldap</li>
<p> and extract it.</p>
<li>Edit <samp>setup.cfg</samp>. The relevant lines are below: 

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">library_dirs = /Developer/SDKs/MacOSX10.5.<span style="color: black;">sdk</span>/usr/lib/
include_dirs = /Developer/SDKs/MacOSX10.5.<span style="color: black;">sdk</span>/usr/include/ /Developer/SDKs/MacOSX10.5.<span style="color: black;">sdk</span>/usr/include/sasl</pre></div></div>

</li>
<li><samp>sudo python setup.py install</samp></li>
<p>That&#8217;s all!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/07/07/getting-python-ldap-installed-on-os-x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My First Conference</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/06/23/my-first-conference/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/06/23/my-first-conference/#comments</comments>
		<pubDate>Mon, 23 Jun 2008 19:58:52 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Velocity08]]></category>
		<category><![CDATA[VelocityConf08]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=28</guid>
		<description><![CDATA[I&#8217;m at Velocity 2008 right now. So far I&#8217;ve learned several things:

Twitter can&#8217;t handle a conference.
Sun might succeed in its attempt to restore its relevance.
SSDs are going to be abother layer in server memory, between disks and RAM.
Whenever a member of the Ops team is unavailable, something breaks.
Con food isn&#8217;t remarkable, in either positive or [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m at <a href="http://en.oreilly.com/velocity2008/public/content/home">Velocity 2008</a> right now. So far I&#8217;ve learned several things:</p>
<ol>
<li>Twitter can&#8217;t handle a conference.</li>
<li>Sun might succeed in its attempt to restore its relevance.</li>
<li>SSDs are going to be abother layer in server memory, between disks and RAM.</li>
<li>Whenever a member of the Ops team is unavailable, something breaks.</li>
<li>Con food isn&#8217;t remarkable, in either positive or negative ways.</li>
<li>Arrive early to talks so you can snag a spot on the power strip.</li>
<li>I FAIL at socialization.</li>
</ol>
<p>At least I managed to score some Puppet schwag (whoever made these shirts has a different definition of XXL than me&#8230;) and managed to track down <a href="http://www.madstop.com/">Luke Kanies</a> and <a href="http://stochasticresonance.wordpress.com/">Andrew Shafer</a> of <a href="http://reductivelabs.com">Reductive Labs</a> to say &#8220;Hi.&#8221; Haven&#8217;t socialized much with them; they look like they&#8217;re working.</p>
<p>About to watch a talk on measuring performance; a topic I&#8217;ve always had big questions about; hopefully I&#8217;ll learn something good. I&#8217;m kinda regretting my decision to sit towards the front, though.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/06/23/my-first-conference/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Crickets</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/06/18/crickets/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/06/18/crickets/#comments</comments>
		<pubDate>Wed, 18 Jun 2008 17:17:17 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Meta]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/?p=27</guid>
		<description><![CDATA[It&#8217;s quiet in here. Life has been hectic and I&#8217;m somewhat confused as to what I&#8217;m allowed to say without getting in trouble at work.
Hopefully I&#8217;ll have some more Puppet content soon.
]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s quiet in here. Life has been hectic and I&#8217;m somewhat confused as to what I&#8217;m allowed to say without getting in trouble at <a href="http://digg.com">work</a>.</p>
<p>Hopefully I&#8217;ll have some more Puppet content soon.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/06/18/crickets/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating Puppet Modules</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/04/18/creating-puppet-modules/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/04/18/creating-puppet-modules/#comments</comments>
		<pubDate>Fri, 18 Apr 2008 23:56:09 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Philosophy]]></category>
		<category><![CDATA[Puppet]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/04/18/creating-puppet-modules/</guid>
		<description><![CDATA[In Puppet parlance, a module is a collection of manifests (including classes and definitions), templates, and files which, taken together, describe a recipe for configuring something via Puppet. Puppet has a number of facilities which make modules incredibly useful and labor-saving.
Modules have a standard internal organization which is described in detail on the ModuleOrganisation wiki [...]]]></description>
			<content:encoded><![CDATA[<p>In <a href="http://puppet.reductivelabs.com">Puppet</a> parlance, a <a href="http://reductivelabs.com/trac/puppet/wiki/GlossaryOfTerms#module">module</a> is a collection of <a href="http://reductivelabs.com/trac/puppet/wiki/GlossaryOfTerms#manifest">manifests</a> (including <a href="http://reductivelabs.com/trac/puppet/wiki/GlossaryOfTerms#class">classes</a> and <a href="http://reductivelabs.com/trac/puppet/wiki/GlossaryOfTerms#definition">definitions</a>), <a href="http://reductivelabs.com/trac/puppet/wiki/GlossaryOfTerms#templates">templates</a>, and files which, taken together, describe a recipe for configuring something via Puppet. Puppet has a number of facilities which make modules incredibly useful and labor-saving.</p>
<p>Modules have a standard internal organization which is described in detail on the <a href="http://reductivelabs.com/trac/puppet/wiki/ModuleOrganisation">ModuleOrganisation</a> wiki page. A trivial module can be as simple as a <samp>manifests</samp> directory containing only a single manifest: <samp>init.pp</samp>. However, as modules grow more complex, you&#8217;ll want to break up your manifests and add templates in a <samp>templates</samp> directory, files in a <samp>files</samp> directory, and a <samp>README</samp> file which explains how to make use of your module.</p>
<p>I&#8217;m in the process of polishing up several modules, including LDAP, Kerberos, memcached, and ntp modules. In some ways, I&#8217;m duplicating work; several implementations of these modules are available. However, Puppet modules are still evolving, and I wanted to try my hand at module writing. Also, the existing modules did not work quite right for us. Some of them fail to properly isolate site-specific information from the recipe, for example. Others had complex interdependencies that I didn&#8217;t like.</p>
<p>There are several techniques that I think will evolve as &#8220;best practices&#8221; for Puppet module design. These are:</p>
<ol>
<li>Keep site-specific customization out of your modules.
<li>Your module should implement a minimal working configuration &#8220;out of the box&#8221;.
<li>Use variables to make it easier to patch your module for use on other platforms.
<li>Break a module up into sensible classes and defined types to make it easy to customize via inheritance.
<li>Use <samp>init.pp</samp> for module-wide variables and/or common functionality, but break all other classes/defines into separate files.
</ol>
<p>Let&#8217;s take a look at each of these in detail:</p>
<h3>Keep site-specific customization out of your modules.</h3>
<p>Modules are, at heart, meant to be shared with others. Try to write your modules so they are as site-neutral as they can be. Use variables that can be set in a higher-level scope like <samp>site.pp</samp> to control how the module works, instead of baking your site&#8217;s settings into the module. For example, in my LDAP module I do this:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> ldap::common <span style="color:#006600; font-weight:bold;">&#123;</span>
  <span style="color:#9966CC; font-weight:bold;">case</span> <span style="color:#ff6633; font-weight:bold;">$ldap_base_dn</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#996600;">&quot;&quot;</span>: <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff6633; font-weight:bold;">$ldap_base_dn</span> = <span style="color:#996600;">&quot;dc=example,dc=com&quot;</span>
      warning<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;ldap_base_dn not set, using default $ldap_base_dn&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">case</span> <span style="color:#ff6633; font-weight:bold;">$ldap_admin_dn</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#996600;">&quot;&quot;</span>: <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff6633; font-weight:bold;">$ldap_admin_dn</span> = <span style="color:#996600;">&quot;cn=admin,$ldap_base_dn&quot;</span>
      warning<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;ldap_admin_dn not set, using default $ldap_admin_dn&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">case</span> <span style="color:#ff6633; font-weight:bold;">$ldap_admin_password</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#996600;">&quot;&quot;</span>: <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#CC0066; font-weight:bold;">fail</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;ldap_admin_password not set!&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span></pre></div></div>

<p>Then, in <samp>site.pp</samp>, I set the variables appropriately:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># Site Variables</span>
<span style="color:#ff6633; font-weight:bold;">$ldap_server</span>           = <span style="color:#996600;">&quot;ash001.example.com&quot;</span>
<span style="color:#ff6633; font-weight:bold;">$ldap_admin_password</span>   = <span style="color:#996600;">&quot;testing&quot;</span>
<span style="color:#ff6633; font-weight:bold;">$ldap_base_dn</span>          = <span style="color:#996600;">&quot;dc=example,dc=com&quot;</span></pre></div></div>

<h3>Your module should implement a minimal working configuration &#8220;out of the box&#8221;.</h3>
<p>This principle is also tied in with the fact that modules were meant to be shared. When someone is looking for a module, they are going to want something that works with little-to-no configuration, because this will give them confidence that the module will work once it is fully configured. Just like full-blown pieces of software, if a module doesn&#8217;t have an easy initial setup, people will get confused as to whether or not it even works. My LDAP module provides a <samp>ldap::master</samp> class which implements a very basic configuration: the slapd package is installed, a working configuration file is set up (without SSL or any of the other goodies), and the service is started. If the user sets just one variable, <samp>$ldap_admin_password</samp> in their <samp>init.pp</samp> and includes <samp>ldap::master</samp> on a node, they will be able to verify that LDAP is up and running with an example configuration. Even better, if they set the other variables, this configuration will be customized to their site with little effort on their part. It might be better to add in all the bells and whistles (SSL at a minimum), but I&#8217;m not sure yet where I stand on that.</p>
<h3>Use variables to make it easier to patch your module for use on other platforms.</h3>
<p>Currently, in the <samp>init.pp</samp> of my LDAP module, I set variables like this:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">  <span style="color:#ff6633; font-weight:bold;">$ldappackage</span>       = <span style="color:#996600;">&quot;slapd&quot;</span>
  <span style="color:#ff6633; font-weight:bold;">$ldapservice</span>       = <span style="color:#996600;">&quot;slapd&quot;</span>
  <span style="color:#ff6633; font-weight:bold;">$ldapdir</span>           = <span style="color:#996600;">&quot;/etc/ldap&quot;</span>
  <span style="color:#ff6633; font-weight:bold;">$ldaputilpackage</span>   = <span style="color:#996600;">&quot;ldap-utils&quot;</span>
  <span style="color:#ff6633; font-weight:bold;">$ldapclientpackage</span> = <span style="color:#996600;">&quot;libnss-ldap&quot;</span></pre></div></div>

<p>and use them like this:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">  package <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#ff6633; font-weight:bold;">$ldappackage</span>:     <span style="color:#9966CC; font-weight:bold;">ensure</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> installed;
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  file <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">&quot;$ldapdir/slapd.conf&quot;</span>:
    content <span style="color:#006600; font-weight:bold;">=&gt;</span> template<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;ldap/slapd.conf.erb&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>,
    <span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> Package<span style="color:#006600; font-weight:bold;">&#91;</span>$ldappackage<span style="color:#006600; font-weight:bold;">&#93;</span>,
    notify  <span style="color:#006600; font-weight:bold;">=&gt;</span> Service<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;$ldapservice&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span>,
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  service <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff6633; font-weight:bold;">$ldapservice</span>:
    <span style="color:#CC0066; font-weight:bold;">require</span>   <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span> Package<span style="color:#006600; font-weight:bold;">&#91;</span>$ldappackage<span style="color:#006600; font-weight:bold;">&#93;</span>, <span style="color:#CC00FF; font-weight:bold;">File</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;$ldapdir/slapd.conf&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">&#93;</span>,
    <span style="color:#9966CC; font-weight:bold;">ensure</span>    <span style="color:#006600; font-weight:bold;">=&gt;</span> running,
    enable    <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span>,
  <span style="color:#006600; font-weight:bold;">&#125;</span></pre></div></div>

<p>These variables are set up for Debian now, because that is the distribution I&#8217;ve standardized on. If later I (or someone I&#8217;ve shared the module with) wants to add support for another distribution, they can set up case statements, and not have to modify the rest of the module!</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">  <span style="color:#9966CC; font-weight:bold;">case</span> <span style="color:#ff6633; font-weight:bold;">$operatingsystem</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    debian: <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldappackage</span>       = <span style="color:#996600;">&quot;slapd&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldapservice</span>       = <span style="color:#996600;">&quot;slapd&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldapdir</span>           = <span style="color:#996600;">&quot;/etc/ldap&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldaputilpackage</span>   = <span style="color:#996600;">&quot;ldap-utils&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldapclientpackage</span> = <span style="color:#996600;">&quot;libnss-ldap&quot;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
    centos: <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldappackage</span>       = <span style="color:#996600;">&quot;openldap&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldapservice</span>       = <span style="color:#996600;">&quot;slapd&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldapdir</span>           = <span style="color:#996600;">&quot;/etc/openldap&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldaputilpackage</span>   = <span style="color:#996600;">&quot;openldap-utils&quot;</span>
      <span style="color:#ff6633; font-weight:bold;">$ldapclientpackage</span> = <span style="color:#996600;">&quot;libnss-ldap&quot;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span></pre></div></div>

<p><em>I have not tested this, as I don&#8217;t use CentOS, so don&#8217;t use these!</em></p>
<h3>Break a module up into sensible classes and defined types to make it easy to customize via inheritance.</h3>
<p>Since you&#8217;ve pulled all the site-specific customization out of your package, and you are providing a minimal working configuration, people are going to want to do other things with your module that are appropriate to their site. If you have everything lumped into one big &#8220;ldap&#8221; class, this is going to be difficult for them. I break thinks up into <samp>ldap::common</samp> for things common to all the other classes, <samp>ldap::client</samp> for things needed to query the LDAP servers, <samp>ldap::master</samp> for the primary LDAP server, and later I&#8217;ll provide <samp>ldap::slave</samp> for replication slaves.</p>
<h3>Use <samp>init.pp</samp> for module-wide variables and/or common functionality, but break all other classes/defines into separate files.</h3>
<p>This is for your sanity as well as the sanity of those who might want to modify your module. I started with everything in one big <samp>init.pp</samp> file and it rapidly went out of control. Puppet does some awesome automagical lookups that you can take advantage of. For example, my <samp>ldap::master</samp> class is defined in a file called <samp>master.pp</samp>; when someone tries to load <samp>ldap::master</samp>, Puppet automatically searches for a <samp>.pp</samp> file named &#8220;master&#8221; in the &#8220;ldap&#8221; module!</p>
<p>So far, this is all the wisdom I have to impart on the subject. I&#8217;ll be sure to post links here when these modules are ready for prime-time.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/04/18/creating-puppet-modules/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SSH tab completion</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/03/24/ssh-tab-completion/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/03/24/ssh-tab-completion/#comments</comments>
		<pubDate>Mon, 24 Mar 2008 17:57:08 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Quick Tips]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/03/24/ssh-tab-completion/</guid>
		<description><![CDATA[This is awesome. Put the following into your ~/.bash_profile file:

complete -W &#34;$(echo `cat ~/.ssh/known_hosts &#124; cut -f 1 -d ' ' \
&#124; sed -e s/,.*//g &#124; uniq &#124; grep -v &#34;\[&#34;`;)&#34; ssh

Open a new shell and type ssh tab tab. I love tab completion, don&#8217;t you?
]]></description>
			<content:encoded><![CDATA[<p>This is awesome. Put the following into your <samp>~/.bash_profile</samp> file:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">complete</span> <span style="color: #660033;">-W</span> <span style="color: #ff0000;">&quot;$(echo <span style="color: #780078;">`cat ~/.ssh/known_hosts | cut -f 1 -d ' ' \
| sed -e s/,.*//g | uniq | grep -v &quot;\[&quot;`</span>;)&quot;</span> <span style="color: #c20cb9; font-weight: bold;">ssh</span></pre></div></div>

<p>Open a new shell and type <samp>ssh</samp> <em>tab tab</em>. I love tab completion, don&#8217;t you?</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/03/24/ssh-tab-completion/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>FreeBSD Update</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/03/17/freebsd-update/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/03/17/freebsd-update/#comments</comments>
		<pubDate>Mon, 17 Mar 2008 18:32:13 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Quick Tips]]></category>
		<category><![CDATA[freebsd]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/03/17/freebsd-update/</guid>
		<description><![CDATA[I just followed these instructions to update my server from FreeBSD 6.2-RELEASE to FreeBSD 7.0-RELEASE.
Aside from taking almost a day, it went flawlessly. I&#8217;m extremely impressed!
]]></description>
			<content:encoded><![CDATA[<p>I just followed <a href="http://www.daemonology.net/blog/2007-11-11-freebsd-major-version-upgrade.html">these instructions</a> to update <a href="http://www.m5hosting.com/">my server</a> from FreeBSD 6.2-RELEASE to FreeBSD 7.0-RELEASE.</p>
<p>Aside from taking almost a day, it went flawlessly. I&#8217;m extremely impressed!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/03/17/freebsd-update/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Virtualization Article</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/03/12/virtualization-article/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/03/12/virtualization-article/#comments</comments>
		<pubDate>Wed, 12 Mar 2008 18:41:13 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[virtualization]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/03/12/virtualization-article/</guid>
		<description><![CDATA[This article might be interesting to some of you.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.protocolostomy.com/2008/03/05/taking-virtualization-to-the-next-level-applogic/">This article</a> might be interesting to some of you.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/03/12/virtualization-article/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bash History</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/03/12/bash-history/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/03/12/bash-history/#comments</comments>
		<pubDate>Wed, 12 Mar 2008 18:35:54 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Meta]]></category>
		<category><![CDATA[Quick Tips]]></category>
		<category><![CDATA[bash]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/03/12/bash-history/</guid>
		<description><![CDATA[Here are a couple of tips for more effective use of your Bash command history. I think the Ctrl-R trick will save me a lot of typing.
I have some articles planned, but have been busy starting my new job. Those few of you who actually read this, don&#8217;t worry &#8211; I&#8217;ll be back.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.ducea.com/2006/05/15/linux-tips-take-control-of-your-bash_history/">Here</a> are a couple of tips for more effective use of your Bash command history. I think the <samp>Ctrl-R</samp> trick will save me a lot of typing.</p>
<p>I have some articles planned, but have been busy starting my new job. Those few of you who actually read this, don&#8217;t worry &#8211; I&#8217;ll be back.</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/03/12/bash-history/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Useful SSH-isms</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/02/23/useful-ssh-isms/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/02/23/useful-ssh-isms/#comments</comments>
		<pubDate>Sat, 23 Feb 2008 22:53:07 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Quick Tips]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/02/23/useful-ssh-isms/</guid>
		<description><![CDATA[Two useful SSH-isms
]]></description>
			<content:encoded><![CDATA[<p><a href="http://m0j0.wordpress.com/2007/09/17/two-ssh-isms-i-forgot-today/">Two useful SSH-isms</a></p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/02/23/useful-ssh-isms/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Simple SSH speed-up</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/02/14/simple-ssh-speed-up/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/02/14/simple-ssh-speed-up/#comments</comments>
		<pubDate>Fri, 15 Feb 2008 00:00:35 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Quick Tips]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/02/14/simple-ssh-speed-up/</guid>
		<description><![CDATA[I just discovered something that is very simple but saves a significant amount of time. Specifically, the SSH ControlMaster feature, which allows you to re-use an existing SSH connection whenever you connect to a host you are already connected to.
In your ~/.ssh/config file, add:

Host *
        ControlMaster auto
  [...]]]></description>
			<content:encoded><![CDATA[<p>I just discovered something that is very simple but saves a significant amount of time. Specifically, the SSH ControlMaster feature, which allows you to re-use an existing SSH connection whenever you connect to a host you are already connected to.</p>
<p>In your <samp>~/.ssh/config</samp> file, add:</p>

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">Host *
        ControlMaster auto
        ControlPath /tmp/%r@%h:%p</pre></div></div>

<p>Whenever you connect to <samp>server.example.com</samp> as user <samp>joeuser</samp>, SSH will create a named pipe at <samp>/tmp/joeuser@server.example.com:22</samp>. If you open another connection to the same server (as the same user), instead of creating a new TCP/IP connection, SSH will automatically multiplex the new session with the existing connection (through the named pipe)! This reduces time spent setting up new connections. Although this usual only saves a couple of seconds per connection, I&#8217;m constantly opening several simultaneous sessions; this will definitely add up to a significant savings over the long term!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/02/14/simple-ssh-speed-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Is it quiet in here?</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/02/11/is-it-quiet-in-here/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/02/11/is-it-quiet-in-here/#comments</comments>
		<pubDate>Mon, 11 Feb 2008 19:37:10 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Meta]]></category>
		<category><![CDATA[blog]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/02/11/is-it-quiet-in-here/</guid>
		<description><![CDATA[The lack of posts is due to a general lull in my work lately. I&#8217;ve got a couple of posts in the queue, but they need more development. Stay tuned!
]]></description>
			<content:encoded><![CDATA[<p>The lack of posts is due to a general lull in my work lately. I&#8217;ve got a couple of posts in the queue, but they need more development. Stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/02/11/is-it-quiet-in-here/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Capistrano</title>
		<link>http://plathrop.tertiusfamily.net/blog/2008/02/03/capistrano/</link>
		<comments>http://plathrop.tertiusfamily.net/blog/2008/02/03/capistrano/#comments</comments>
		<pubDate>Sun, 03 Feb 2008 22:51:58 +0000</pubDate>
		<dc:creator>plathrop</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[capistrano]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://plathrop.tertiusfamily.net/blog/2008/02/03/capistrano/</guid>
		<description><![CDATA[One of the things you quickly learn as you become a more sophisticated systems administrator is that performing the same task on several servers is both tedious and error-prone. Of course, if your infrastructure has been deployed in an ad-hoc manner, sometimes you don&#8217;t have a choice; the task is unique on each machine simply [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things you quickly learn as you become a more sophisticated systems administrator is that performing the same task on several servers is both tedious and error-prone. Of course, if your infrastructure has been deployed in an ad-hoc manner, sometimes you don&#8217;t have a choice; the task is unique on each machine simply because each machine has a unique configuration. However, once you have made the transition to stock configurations on multiple machines, you gain the ability to use new tools to perform tasks in parallel on a number of systems at once. The tendency for many admins is to <a href="http://sial.org/howto/openssh/publickey-auth/">set up public-key authentication</a> and hand-roll a script. This isn&#8217;t necessarily a bad approach; sometimes a quick-and-dirty script is all you really need to get the job done. On the other hand, there are tools which can make the process more elegant, consistent, and professional. The two I know of are <a href="http://sourceforge.net/projects/clusterssh/">ClusterSSH</a> and <a href="http://www.capify.org">Capistrano</a>.</p>
<p>I&#8217;m fairly new to Capistrano, but I&#8217;ve been using it to perform some simple tasks for a couple of months, and I&#8217;m really pleased with the results. I first encountered Capistrano when I was looking for a simple way to remove the SNMP agent from a group of about 20 machines. I knew I could whip up a script, but I had remembered reading a blog post somewhere (sorry, I don&#8217;t remember where) about ClusterSSH; several Google searches later I had found both ClusterSSH and Capistrano. At the time, I was learning Ruby (in order to get involved in <a href="http://puppet.reductivelabs.com">Puppet</a> development), so Capistrano seemed the natural choice.</p>
<p>According to the website, Capistrano was originally written to help deploy Ruby on Rails applications. Like many tools, it has grown beyond its initial mission, and is now a powerful tool for systems administration. Capistrano makes some assumptions about your infrastructure. First, you must be using SSH to access your systems, because Capistrano does not support older methods such as telnet (and if you are still using telnet, shame on you!) Second, your systems must have a POSIX shell in the default system path. For most *nix systems these days, this is a given. Finally, to *really* utilize Capistrano correctly, you should be using public-key authentication to access your servers. You don&#8217;t necessarily need to use password-less keys (in fact, I suggest you resist that temptation in general). Just use ssh-agent to keep your passphrase in memory. All of these assumptions are true for my environment, so I got started.</p>
<p>Installation was trivial using <a href="http://www.rubygems.org/">RubyGems</a>. Next I needed to create a &#8220;capfile&#8221; &#8211; the Capistrano equivalent of a &#8220;Makefile&#8221;. The &#8220;capfile&#8221; gives Capistrano information about your environment and defines &#8220;roles&#8221; and &#8220;tasks&#8221;. &#8220;Roles&#8221; describe groups of systems, &#8220;webservers&#8221; or &#8220;firewalls&#8221; for example. &#8220;Tasks&#8221; are the actions you wish to perform. After reading through the <a href="http://www.capify.org/getting-started/basics">Basics</a> on the Capistrano website, it was fairly simple to define a task to do what I wanted:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">task <span style="color:#ff3333; font-weight:bold;">:remove_snmp</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  run <span style="color:#996600;">&quot;sudo aptitude -y purge snmp&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>One complication of my environment is that most of the systems are not directly accessible from outside. We have an SSH &#8220;bastion host&#8221; which acts as a gateway to the internal network. Luckily, Capistrano is ready for this. I added the following to the beginning of my capfile:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">set <span style="color:#ff3333; font-weight:bold;">:gateway</span>, <span style="color:#996600;">&quot;ssh-gateway.example.com&quot;</span></pre></div></div>

<p>This tells Capistrano to first establish an SSH connection to <samp>ssh-gateway.example.com</samp>, and connect from that machine to the systems we want to run commands on. &#8220;But how does Capistrano know what systems to run commands on?&#8221; you ask. Good question! That is what we need a &#8220;role&#8221; for:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">role <span style="color:#ff3333; font-weight:bold;">:de_snmp</span>, <span style="color:#996600;">&quot;server1.example.com&quot;</span>, <span style="color:#996600;">&quot;server2.example.com&quot;</span>, <span style="color:#996600;">&quot;server3.example.com&quot;</span>, <span style="color:#996600;">&quot;server4.example.com&quot;</span>, <span style="color:#996600;">&quot;server5.example.com&quot;</span>, <span style="color:#996600;">&quot;server6.example.com&quot;</span></pre></div></div>

<p>Before you run a task on all of your systems at once, you probably want to test it first. I usually try the task by hand on one or two systems. Once I am happy with the procedure and the results, I pick several less-critical (or easy to rollback) hosts to do a second pass on. Finally, after all is well with those hosts, I run the task on the full group. Here is our completed &#8220;capfile&#8221; for the second pass:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;">set <span style="color:#ff3333; font-weight:bold;">:gateway</span>, <span style="color:#996600;">&quot;ssh-gateway.example.com&quot;</span>
&nbsp;
role <span style="color:#ff3333; font-weight:bold;">:de_snmp</span>, <span style="color:#996600;">&quot;server1.example.com&quot;</span>, <span style="color:#996600;">&quot;server2.example.com&quot;</span>, <span style="color:#996600;">&quot;server3.example.com&quot;</span>, <span style="color:#996600;">&quot;server4.example.com&quot;</span>, <span style="color:#996600;">&quot;server5.example.com&quot;</span>, <span style="color:#996600;">&quot;server6.example.com&quot;</span>
&nbsp;
task <span style="color:#ff3333; font-weight:bold;">:remove_snmp</span>, <span style="color:#ff3333; font-weight:bold;">:roles</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:de_snmp</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  run <span style="color:#996600;">&quot;sudo aptitude -y purge snmp&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>As you can see, we&#8217;ve modified our &#8220;task&#8221; definition to apply to the &#8220;role&#8221; we created. All we have to do now is run <samp>cap remove_snmp</samp> and Capistrano will do its job! There is plenty of output, so it is fairly easy to review what is happening (though it can be challenging to follow the events on a single server unless you know your way around <samp>grep</samp>; you do, right?)</p>
<p>This is just the tip of the iceberg. Capistrano is very sophisticated, allowing your tasks to be self-documenting, divided into namespaces, use variables, and more! Perhaps the most powerful feature is the ability to define &#8220;transactions&#8221; and &#8220;rollback&#8221; functions. Although you still have to manually define what a &#8220;rollback&#8221; means, Capistrano allows you to do that once, and use the capability as often as necessary. In addition, you have the full power of Ruby at your command in Capistrano scripts. I haven&#8217;t had the need to explore these features but as I do I&#8217;ll be sure to share my experiences here!</p>
]]></content:encoded>
			<wfw:commentRss>http://plathrop.tertiusfamily.net/blog/2008/02/03/capistrano/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
