published on 6 May 2011

The goal of this design pattern is to keep configurable data in one place and make it friendly to an ENC. This addresses the problem of module reusability. If you have are keeping data in your module, then someone else cannot just reuse your module without making changes to the code. This pattern solves that problem allowing for reusable modules.

A best practice is to have a module named 'common' that contains code that is used by all of your nodes. This class is then included in all of your nodes.

node '' {
 $env = 'prod'
 include common

node '' {
 $env = 'prod'
 include common

I have begun using a subclass of common, named common::data that contains data about your systems and environments. It is called from the common class
as well as any class that needs to access that data.

class common {
 include common::data

class common::data {

 # Time - you should have 2n+1 time servers
 $ntpServerList = [ '', '',
'' ]

 # RHN
 $satelliteServer = ''

 # LSB Provider Name -
 $lsbProvider = "puppetlabs"

 # address to which commits to the puppet code will be delivered
 $puppetCommitsMailAddress = 'puppet-commits@foo.tld'

 # DNS
 $dnssearchpath = 'foo.tld'

 # ensure that the $env variable has been set
 # valid values are: 'lab', 'dev', 'qa', 'prod'
 if ! ( $env in [ 'lab', 'dev', 'qa', 'prod' ] ) {
   fail("common::data env must be one of: 'lab', 'dev', 'qa', or 'prod'")
 } # fi

 # environment specific data
 case $env {
   'lab': {
     $nameservers = [ '', '' ]
     $appFiler = ''
   } # lab:
   'dev': {
     $nameservers = [ '', '' ]
     $appFiler = ''
   } # dev:
   'qa': {
     $nameservers = [ '', '' ]
     $appFiler = ''
   } # qa:
   'prod': {
     $nameservers = [ '', '' ]
     $appFiler = ''
   } # prod:
   default: {
     fail("common::data env must be one of: 'lab', 'dev', 'qa', or 'prod'")
   } # default:
 } # case $env

 $contactEmail = $env ? {
   # default should be non-prod systems.
   default => 'preprod-root-list@foo.tld',
   # mail for prod systems
   prod    => 'root-list@foo.tld',
 } # $contactEmail
} # class common::data

From the above code you see that we are setting site wide data as well as using conditionals to set more specific data, specifically handling
environments using a case statement around a $env variable You could also base this on data center or whatever is specific to your setup.

Now to use that code in your manifests.

class dnsclient {

 include common::data

 $nameservers   = $common::data::nameservers
 $dnssearchpath = $common::data::dnssearchpath

 if ! $common::data::nameservers {
   fail("The variable 'common::data::nameservers' must be set")
 } # fi

 if ! $common::data::dnssearchpath {
   fail("The variable 'common::data::dnssearchpath' must be set")
 } # fi

 file { '/etc/resolv.conf':
   content => template('dnsclient/resolv.conf.erb'),
 } # file
} # class dnsclient

We are including common::data to ensure that the data is set before we attempt to access it. Next we verify that the variables contain data and fail the catalog if they do not. We can then use the variables in the template as follows.


# This file is being maintained by Puppet.

search <%= dnssearchpath %>
<% nameservers.each do |nameserver| -%>
nameserver <%= nameserver %>
<% end -%>

This gives you one entry point in your code to specify data as opposed to repeating those variables in every node definition or storing them in the individual modules, since that would make them harder to re-use and share.

I encourage you to use this design pattern to make your modules reusable while allowing for data to be stored in one central location.

Share via:
Posted in:

Add new comment

The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.