<?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>Bode&#039;s Blog</title>
	<atom:link href="http://bodepd.com/wordpress/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://bodepd.com/wordpress</link>
	<description>YAWB</description>
	<lastBuildDate>Sun, 24 Oct 2010 06:30:58 +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>Proposal: Managing Puppet&#8217;s configuration data</title>
		<link>http://bodepd.com/wordpress/?p=64</link>
		<comments>http://bodepd.com/wordpress/?p=64#comments</comments>
		<pubDate>Sun, 24 Oct 2010 06:25:29 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://bodepd.com/wordpress/?p=64</guid>
		<description><![CDATA[There are many best practices and standards for module development that need to be identified and agreed upon before modules can be truly reusable.
One of the most urgent needs is a well defined process for configuration data organization and overrides.
Fortunately, progress has already been made towards this goal which I can build upon&#8230;
My experiment expands [...]]]></description>
			<content:encoded><![CDATA[<p>There are many best practices and standards for module development that need to be identified and agreed upon before modules can be truly reusable.</p>
<p>One of the most urgent needs is a well defined process for configuration data organization and overrides.</p>
<p>Fortunately, progress has already been made towards this goal which I can build upon&#8230;</p>
<p>My experiment expands on the work by R.I. Pienaar on <a href=http://www.devco.net/archives/2009/08/31/complex_data_and_puppet.php>extlookup</a>.</p>
<p>Tons of ideas borrowed from conversations I&#8217;ve had with Alessandro Franceschi which are outlined in his <a href='http://www.lab42.it/presentations/puppetmodules/puppetmodules.html'>presentation for Puppetcamp</a></p>
<h2> Goal: </h2>
<h3> separation of configurable data from puppet manifests: </h3>
<ul>
<li> puppet manifests &#8211; Puppet resources, dependencies, and language elements.
<li> configurable data &#8211; data that could differ between runs of the same manifests.
</ul>
<ol>
<li> facilitates well defined data override structure
<ul>
<li> allows users to interact with modules in a well-defined way.</p>
<li> allows modules to be used without reading/modifying the source code (encapsulation of logic through a well defined interface).
</ul>
<li> easier to manage and override platforms where differences can be described as data.
<ul>
<li> ie: debian-ish v redhat-ish </ul>
<li> allows finer grain access control.
<ul>
<li>ie: some users (devops) have permission to change manifests, while other users (operators) have limited permissions to change configuration data related to a specific environment (ie: production/datacenter1).</ul>
</ol>
<h2> Proposed solution: </h2>
<p>well defined override precedence:</p>
<ol>
<li> modules should be packaged with a data store.
<ul>
<li> introduce new module directory &lt;modulename/data/&gt;
<li> default data can be shipped with modules and stored separately from configuration logic.
<li> module data store should allow fact/variable based overrides (ie: %{operatingsystem} overrides default, like extlookup}) .
  </ul>
<li> allow per site data stores that override a module&#8217;s data store.
<ul>
<li> introduce a single directory that can be used to override configuration.</p>
<ul>
<li> &lt;/etc/puppet/site-overrides/modulename/data&gt; &#8211; overrides should mimic module structure.</p>
<li> overrides path should be similar to module datastore</ul>
</ul>
<li> all data is managed as attributes for parameterized classes
<ul>
<li> allows all data to be overridden during class declaration.</ul>
</ol>
<p>version 2.6.x of Puppet actually brings us most of the way towards this solution with the following new features:</p>
<ul>
<li> extlookup</p>
<li> parameterized classes</ul>
<h2> Example: </h2>
<p>I have chosen to implement a simple motd module to demonstrate the proposed data management solution:</p>
<h3> Components: </h3>
<ol>
<li> module layout:
<pre>
|-- data
|   |-- CentOS.csv
|   `-- default.csv
|-- manifests
|   |-- init.pp
|-- templates
|   `-- motd.erb
`-- tests
    |-- init.pp
</pre>
<li> data override layout
<pre>
site-overrides/
`-- motd
    `-- data
        `-- CentOS.csv
</pre>
<li> templates/motd.erb<br />
Templates allow an easy way to see the resulting values of variables.</p>
<pre>
DefaultData <%= default_data %>
MoreData <%= more_data %>
LocalOveride <%= local_override %>
OverrideData <%= override_data %>
</pre>
<li> manifests/init.pp
<ul>
<li> simple class that specifies all data as class attributes:</p>
<li> all data defaults to the value derived from extlookup
<li> all data can be overriden when class is declared.</ul>
<pre>
class motd (
    $default_data = extlookup('default_data'),
    $more_data = extlookup('more_data'),
    $override_data = extlookup('override_data'),
    $local_override = extlookup('local_override')
 ){
  file { '/etc/motd':
    content => template('motd/motd.erb'),
  }
}
</pre>
<li> tests/init.pp
<ul>
<li>specify lookup precedence for extlookup</p>
<li>declare class and specify highest precedence overrides.
</ul>
<pre>
$extlookup_datadir = '/'
$local_overrides = '/etc/puppet/site'
$extlookup_precedence = [
  "${local_overrides}/%{module_name}/data/%{operatingsystem}",
  "${local_overrides}/%{module_name}/data/default",
  "%{module_data_dir}/%{operatingsystem}",
  "%{module_data_dir}/default"
  ]
class { 'motd':
  override_data => 'override'
}
</pre>
</ol>
<h3>precedence of data lookup</h3>
<ol>
<li> default to extlookup value: use specified precendence</p>
<ol>
<li>/etc/puppet/modules/motd/data/default</p>
<pre>
default_data, default
local_override, default
</pre>
<li>/etc/puppet/modules/motd/data/CentOS
<pre>
more_data, centos
override_data, centos
</pre>
<li>/etc/puppet/site-overrides/motd/data/default
<li>/etc/puppet/site-overrides/motd/data/CentOS
<pre>
local_override, local_centos
</pre>
</ol>
<li> class parameters override everything
<pre>
class { 'motd':
  override_data => 'override'
}
</ol>
</pre>
<h3>Results</h3>
<p>:</p>
<pre>
#>puppet apply tests/init.pp
#>cat /etc/motd
DefaultData  default
MoreData  centos
LocalOveride  local_centos
OverrideData override
</pre>
<h3>Changes to Puppet:</h3>
<p>This example required the following changes to Puppet core:</p>
<ol>
<li>patch to extlookup</p>
<ul>
<li> allow multiple puppet variables to be interpolated.</ul>
<li> patch to puppet (Puppet::Resource::Type)
<ul>
<li> create magic variable in Puppet::Resource::Type that can find the current manifest&#8217;s module&#8217;s data dir &#8211; module_data_dir</ul>
<ul>
<li>move the code where magic variables(module_name, module_data_dir) are added to a classes scope so they will be available before parameter defaults are evaluated.</ul>
</ol>
<p>branch of code from 2.6.3rc1 at:</p>
<p>http://github.com/bodepd/puppet/tree/data-hack</p>
<p>This code requires more significant changes to puppet:</p>
<ul>
<li> external node classifier interface needs to support parameterized classes.</ul>
<h2>Improvements:</h2>
<ul>
<li> puppet.conf (and environments) should support an option for the following:</p>
<ul>
<li> site_override_dir &#8211; directory where local override modules are found.</p>
<ul>
<li> default &#8211; /etc/puppet/site-overrides/</ul>
</ul>
<ul>
<li> lookup_precendence &#8211; order expressing how files are looked up</p>
<ul>
<li> default-%{operatingsystem}:default</ul>
</ul>
</ul>
<h2>Additional requirements:</h2>
<h3>Ability to resolve data from a class. </h3>
<ul>
<li> A modules configuration needs to be readable by other modules.                                                               </p>
]]></content:encoded>
			<wfw:commentRss>http://bodepd.com/wordpress/?feed=rss2&amp;p=64</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Using puppet&#8217;s logging facility</title>
		<link>http://bodepd.com/wordpress/?p=61</link>
		<comments>http://bodepd.com/wordpress/?p=61#comments</comments>
		<pubDate>Thu, 12 Aug 2010 23:40:47 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://bodepd.com/wordpress/?p=61</guid>
		<description><![CDATA[When I am writing code that works together with puppet, I often want to just use Puppet&#8217;s logging facility.
I can use puppet&#8217;s logging to log to syslog with the following code.

require puppet
Puppet.parse_config
Puppet::Util::Log.level = :info
Puppet::Util::Log.newdestination(:syslog)
Puppet.warning('dude!!!')
Puppet.warning('sweet!!)'

]]></description>
			<content:encoded><![CDATA[<p>When I am writing code that works together with puppet, I often want to just use Puppet&#8217;s logging facility.</p>
<p>I can use puppet&#8217;s logging to log to syslog with the following code.</p>
<pre>
require puppet
Puppet.parse_config
Puppet::Util::Log.level = :info
Puppet::Util::Log.newdestination(:syslog)
Puppet.warning('dude!!!')
Puppet.warning('sweet!!)'
</pre>
]]></content:encoded>
			<wfw:commentRss>http://bodepd.com/wordpress/?feed=rss2&amp;p=61</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Managing EC2 nodes with Puppet</title>
		<link>http://bodepd.com/wordpress/?p=27</link>
		<comments>http://bodepd.com/wordpress/?p=27#comments</comments>
		<pubDate>Wed, 02 Jun 2010 01:05:41 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[puppet]]></category>
		<category><![CDATA[ec2]]></category>

		<guid isPermaLink="false">http://bodepd.com/wordpress/?p=27</guid>
		<description><![CDATA[I recently spent a little time using puppet to model EC2 deployment.
The goal is to use Puppet&#8217;s concise language and its capability as a dependency manager to model the order in which I need to install nodes into Amazon&#8217;s Elastic Compute Cloud.
the code can be found in its entirety at 
http://github.com/bodepd/puppet-ec2 .
Noder Type:
The first step [...]]]></description>
			<content:encoded><![CDATA[<p>I recently spent a little time using puppet to model EC2 deployment.</p>
<p>The goal is to use Puppet&#8217;s concise language and its capability as a dependency manager to model the order in which I need to install nodes into Amazon&#8217;s Elastic Compute Cloud.</p>
<p>the code can be found in its entirety at </p>
<p><a href='http://github.com/bodepd/puppet-ec2'>http://github.com/bodepd/puppet-ec2</a> .</p>
<h3>Noder Type:</h3>
<p>The first step is to create a type that will define the interface for deployment.</p>
<p>The type that I have created is called &#8220;noder&#8221; since &#8216;node&#8217; is already a reserved word in Puppet.</p>
<pre>
Puppet::Type.newtype(:noder) do ...
</pre>
<p>The following parameters are added to the type using the method: newparam()</p>
<ul>
<li>name &#8211; unique name that will represent this EC2 instance.</li>
<li>user &#8211; Amazon Web services user account.</li>
<li>password &#8211; password associated with the user account.</li>
<li>image &#8211; is the name of the instance&#8217;s base image(AMI).</li>
<li>description &#8211; description associated with this ec2 instance.</li>
<li>type &#8211; Amazon term that determines the specifications for the instance.</li>
</ul>
<h3>Modeling the provider</h3>
<h4>EC2 Namevar:</h4>
<p>There is a significant inconsistency between how puppet models resources and how EC2 works.</p>
<p>Puppet needs to know the namevar for a resource in order to manage it. This means that puppet needs to be able to specify an ID for an EC2 instance before that instance exists, and then be able to query the instance based on this ID.</p>
<p>In EC2, the unique ID for an instance can only be determined after the instance is actually created.</p>
<p>There is at least one way to get around this limitation of EC2 (even though its a little hackish, suggestions are welcome <img src='http://bodepd.com/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ) Thanks to Jesse for helping me figure this out.</p>
<p>I will ensure that there is a 1-1 relationship between all managed EC2 instances and a security group whose name is prefixed with PUPPET_. This allows me to use the name of this security group as my namevar.</p>
<h4>self.prefetch</h4>
<p>Any logic that needs to be performed once per resource provider and before the ensurable methods are called should be performed in self.prefetch.</p>
<p>I will use self.prefetch to pre-generate both EC2 connections as well as the state of all EC2 instances once per provider (as opposed to once per resource).</p>
<p>I will store this information in the following class variables:</p>
<ul>
<li>@ec2 &#8211; hash of EC2 user account =&gt; EC2 connection.</li>
<li>@instances &#8211; hashes each instance not in the &#8216;terminated&#8217; or &#8217;shutting-down&#8217; state as follows:</li>
<ul>
<li>key: name of unique security group used as the instances namevar.</li>
<li>value:  hash of the following:</li>
<ul>
<li>key: :instance_id = real ec2 instance id</li>
<li>value: :state = current state of instance when self.prefetch is called.</li>
</ul>
</ul>
</ul>
<h4>Ensurable:</h4>
<p>Since the noder type was specified as ensurable, I need to define the methods: &#8216;exists?&#8217;, &#8216;create&#8217; and &#8216;destroy&#8217;.</p>
<h4>Exists?:</h4>
<p>Since I have already stored the current state of our instances in @instances, I can just query the hash for a given instances state as follows:</p>
<pre>
def exists?
  state = self.class.instance_state(@resource.value(:name))
  state == 'running' || state == 'pending'
end
</pre>
<h4>Create:</h4>
<p>if ensure =&gt;present and exists? returns false, ie: the instance either does not exist, or is not in the running or pending state, then I will run the create method.</p>
<p>In the create method, I can just retrieve the ec2 connection that I need by calling</p>
<pre>
ec2 =  self.class.ec2(@resource.value(:user))
</pre>
<p>this returns the ec2 connection associated with my user parameter.</p>
<p>There are two things that I need to do on creation.</p>
<p>1. If a security group with my namvaer does not already exist, I need to create it.</p>
<pre>
ec2 = self.class.ec2(@resource.value(:user))
group = @resource.value(:name)
begin
  ec2.describe_security_groups({:group_name =&gt; group})
  rescue Exception =&gt; e
    unless self.class.instance_state(group)
      ec2.create_security_group(
      {
        :group_name =&gt; group,
        :group_description =&gt; @resource.value(:desc)
      }
  end
end
</pre>
<p>2. Once I have ensured that the group exists, I call ec2.run_instances, specifying the AMI image, security group (namevar), and type.</p>
<pre>
ec2.run_instances(
  {
    :image_id =&gt; @resource.value(:image),
    :security_group =&gt; group,
    :instance_type =&gt; @resource.value(:type),
  }
)
</pre>
<h4>Destroy:</h4>
<p>if ensure =&gt;absent and exists? returns true, then I need to run the &#8216;destroy&#8217; method.</p>
<p>I can use the class method instance_id to retrieve the actual EC2 instance id from existing instances.</p>
<p>Then I will delete both the instance as well as the group that I am using to determine its namer.</p>
<pre>
def destroy
  group = @resource.value(:name)
  ec2 =  self.class.ec2(@resource.value(:user))
  instance = self.class.instance_id(group)
  ec2.terminate_instances({:instance_id =&gt; instance})
  ec2.delete_security_group({:group_name =&gt; group})
end
</pre>
<h4>Usage</h4>
<p>some usage examples:</p>
<pre>
noder { 'blah2':
  ensure => present,
  user => 'Dannyboy',
  password => 'password',
  image => 'ami-84db39ed',
  desc => 'happy instance',
}
noder { 'blah3':
  ensure => present,
  user => 'Dannyboy',
  password => 'password',
  image => 'ami-84db39ed',
  desc => 'happy instance',
}
noder { 'default':
  ensure => present,
  user => 'Dannyboy',
  password => 'password',
  image => 'ami-84db39ed',
  desc => 'happy instance',
  require => Noder['blah3']
}
noder { 'blah':
  ensure => absent,
  user => 'Dannyboy',
  password => 'password',
  image => 'ami-84db39ed',
  desc => 'happy instance',
  require => Noder['default'],
}
</pre>
<h4>Next Steps:</h4>
<p>Now that I have the basics working, the next step is to add more features:</p>
<p>1. Specify image restart on refresh<br />
2. Consider better ways to synchronize with EC2 state.<br />
3. Add suspend, stop ensure states.<br />
4. Switch params to properties once its supported by the ec2 ruby bindings.</p>
]]></content:encoded>
			<wfw:commentRss>http://bodepd.com/wordpress/?feed=rss2&amp;p=27</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Managing certs for clustered puppetmasters</title>
		<link>http://bodepd.com/wordpress/?p=7</link>
		<comments>http://bodepd.com/wordpress/?p=7#comments</comments>
		<pubDate>Tue, 01 Jun 2010 23:12:03 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[puppet]]></category>
		<category><![CDATA[ca]]></category>
		<category><![CDATA[ssl]]></category>

		<guid isPermaLink="false">http://bodepd.com/wordpress/?p=7</guid>
		<description><![CDATA[Motivation:
It&#8217;s common to deploy multiple puppetmasters behind a load-balancer.  In this setup, it is necessary to specify a single trusted certificate authority so that all clients can seamlessly connect to any of the clustered puppetmasters.
Example:
This example will use two hosts:

puppet1 -  will serve as the CA, puppetmaster1, and puppetclient1 (alias: puppetmaster3)
puppet2 &#8211; will not serve [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Motivation:</strong></p>
<p>It&#8217;s common to deploy multiple puppetmasters behind a load-balancer.  In this setup, it is necessary to specify a single trusted certificate authority so that all clients can seamlessly connect to any of the clustered puppetmasters.</p>
<p><strong>Example:</strong></p>
<p>This example will use two hosts:</p>
<ol>
<li>puppet1 -  will serve as the CA, puppetmaster1, and puppetclient1 (alias: puppetmaster3)</li>
<li>puppet2 &#8211; will not serve as the CA, puppetmaster2, puppetclient2 (alias: puppetmaster4)</li>
</ol>
<p><strong>CA</strong></p>
<p>The command, puppetca, run with no arguments will create a CA.</p>
<pre>
puppet1&gt;puppetca
puppet1 ssl]# find -type f
ssl/ca/serial
ssl/ca/ca_key.pem
ssl/ca/private/ca.pass
ssl/ca/ca_crt.pem
ssl/ca/ca_crl.pem
ssl/ca/inventory.txt
</pre>
<p><strong>Generating ssl certs</strong></p>
<p>Now, use puppetca &#8211;generate to create the ssl certificates for all puppetmasters. Additional aliases can also be specified as an option.</p>
<pre>
puppet1&gt;puppetca --generate --certdnsnames puppetmaster3:puppet puppetmaster1
puppet1&gt;puppetca --generate --certdnsnames puppetmaster4:puppet puppetmaster2
puppet1&gt;find ssl | grep master
ssl/private_keys/puppetmaster1.pem
ssl/private_keys/puppetmaster2.pem
ssl/ca/signed/puppetmaster1.pem
ssl/ca/signed/puppetmaster2.pem
</pre>
<p>Copy the signed certificate, private key, and certificate for the trusted CA to <em>puppet2</em>.</p>
<p>First create the necessary directories on <em>puppet2</em>:</p>
<pre>
puppet2&gt;cd /var/lib/puppet/
puppet2&gt;mkdir -p ssl/private_keys ssl/certs
</pre>
<p>Now copy the files.</p>
<pre>
puppet1&gt;cd /var/lib/puppet/ssl
puppet1&gt;scp private_keys/puppetmaster2.pem root@puppet2:/var/lib/puppet/ssl/private_keys/puppetmaster2.pem
puppet1&gt;scp ca/signed/puppetmaster2.pem root@puppet2:/var/lib/puppet/ssl/certs/puppetmaster2.pem
puppet1&gt;scp ca/ca_crt.pem root@puppet2:/var/lib/puppet/ssl/certs/ca.pem
</pre>
<p><strong>Start puppetmasters:</strong></p>
<p>Start both puppetmasters in the foreground</p>
<p>on puppet1</p>
<pre>
#puppet.conf
[puppetmasterd]
  certname=puppetmaster1
  ca=true
</pre>
<pre>
puppet1&gt;puppetmasterd --no-daemonize --verbose --certname puppetmaster1
info: Caching certificate for ca
info: Caching certificate for puppetmaster1
</pre>
<p>now on puppet2:</p>
<pre>
#puppet.conf
[puppetmasterd]
  certname=puppetmaster2
  ca=false
</pre>
<pre>
puppet2&gt;puppetmasterd --no-daemonize --verbose --certname puppetmaster2
</pre>
<p><strong>Clients:</strong></p>
<p>on both clients, configure the following:</p>
<pre>
#puppet1 puppet.conf
[puppetd]
  certname=puppetclient1
  ca_server=puppet1
</pre>
<pre>
#puppet2 puppet.conf
[puppetd]
  certname=puppetclient2
  ca_server=puppet1
</pre>
<p>Now, create the file /etc/puppet/manifests/site.pp.</p>
<pre>
# site.pp
notify{$servername:}
</pre>
<p>before testing, I will set up dns in /etc/hosts file.</p>
<pre>
# /etc/hosts
192.168.0.2 puppet puppetmaster2 puppetmaster4 puppetclient2
192.168.0.3 puppetmaster1 puppetmaster3 puppetclient1
</pre>
<p>now, run the following:</p>
<pre>
puppet1&gt;puppetd --test --server puppetmaster1
puppet1&gt;puppetd --test --server puppetmaster2
puppet1&gt;puppetd --test --server puppetmaster3
puppet1&gt;puppetd --test --server puppetmaster4
</pre>
<pre>
puppet2&gt;puppetd --test --server puppetmaster1
puppet2&gt;puppetd --test --server puppetmaster2
puppet2&gt;puppetd --test --server puppetmaster3
puppet2&gt;puppetd --test --server puppetmaster4
</pre>
<p>Thus illustrating that the clients can connect to either puppetmaster by any of their allowed dns names.</p>
]]></content:encoded>
			<wfw:commentRss>http://bodepd.com/wordpress/?feed=rss2&amp;p=7</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hello world!</title>
		<link>http://bodepd.com/wordpress/?p=1</link>
		<comments>http://bodepd.com/wordpress/?p=1#comments</comments>
		<pubDate>Mon, 31 May 2010 20:01:22 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://dan.birringer.at/wordpress/?p=1</guid>
		<description><![CDATA[Well, after years and years of putting it off, I finally started a blog. Partially in response to the fact that I actually have stuff to talk about that people want to learn  
]]></description>
			<content:encoded><![CDATA[<p>Well, after years and years of putting it off, I finally started a blog. Partially in response to the fact that I actually have stuff to talk about that people want to learn <img src='http://bodepd.com/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://bodepd.com/wordpress/?feed=rss2&amp;p=1</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

