Professional Documents
Culture Documents
Jeremy Kitchen
Systems Engineer at NationBuilder
What is a module?
Package
Config
Service
Module Contents
foo/
manifests/
defaults.pp
init.pp
install.pp
config.pp
service.pp
templates/
foo.conf.erb
manifests/defaults.pp
class foo::defaults {
case $osfamily {
'Debian': {
$package_name = 'foo-ruby'
$service_name = 'foo'
$config_path = '/etc/foo/foo.conf'
$log_file = '/var/log/foo/foo.log'
$storage_path = '/var/lib/foo'
}
'RedHat': {
$package_name = 'ruby-foo'
$service_name = 'foo'
$config_path = '/etc/foo.conf'
$log_file = '/var/log/foo.log'
$storage_path = '/var/lib/foo'
}
default: {
fail("${osfamily} not currently supported by this module")
}
}
}
manifests/init.pp
class foo (
$package_ensure = installed,
$listen = '127.0.0.1',
$port = '1234',
$verbose = false,
) inherits foo::defaults {
if (!is_ip_address($listen)) {
fail('listen parameter needs to be an ip address')
}
$verbose_bool = str2bool($verbose)
include foo::install
include foo::config
include foo::service
anchor {
'foo::begin':
before => Class['foo::install','foo::config'],
notify => Class['foo::service'];
'foo::end':
require => Class['foo::service'];
}
}
manifests/init.pp
class foo (
$package_ensure = installed,
$listen = '127.0.0.1',
$port = '1234',
$verbose = false,
) inherits foo::defaults {
manifests/init.pp
if (!is_ip_address($listen)) {
fail('listen parameter needs to be an ip address')
}
$verbose_bool = str2bool($verbose)
manifests/init.pp
include foo::install
include foo::config
include foo::service
anchor {
'foo::begin':
before => Class['foo::install','foo::config'],
notify => Class['foo::service'];
'foo::end':
require => Class['foo::service'];
}
containment example
include site::mounts
include site::webapp
include mysql
include apache
anchor {
'foo::begin':
before => Class['foo::install','foo::config'],
notify => Class['foo::service'];
'foo::end':
require => Class['foo::service'];
}
what about contain?
manifests/init.pp
class foo (
$package_ensure = installed,
$listen = '127.0.0.1',
$port = '1234',
$verbose = false,
) inherits foo::defaults {
if (!is_ip_address($listen)) {
fail('listen parameter needs to be an ip address')
}
$verbose_bool = str2bool($verbose)
include foo::install
include foo::config
include foo::service
anchor {
'foo::begin':
before => Class['foo::install','foo::config'],
notify => Class['foo::service'];
'foo::end':
require => Class['foo::service'];
}
}
manifests/install.pp
[main]
listen = <%= @listen %>
port = <%= @port %>
verbose = <%= @verbose_bool ? 'yes' : 'no' %>
log_file = <%= @log_file %>
storage_dir = <%= @storage_dir %>
Module Contents
foo/
manifests/
defaults.pp
init.pp
install.pp
config.pp
service.pp
templates/
foo.conf.erb
example
# profile/manifests/server.pp
class profile::server {
class { 'foo':
port => 3389,
verbose => true,
}
}
example
# hiera/global.yaml
foo::port: 3389
foo::verbose: true
# profile/manifests/server.pp
class profile::server {
include ::foo
}
conf.d
Module Contents
foo/
manifests/
defaults.pp
init.pp
install.pp
config.pp
service.pp
plugin.pp
templates/
foo.conf.erb
plugin.conf.erb
manifests/defaults.pp
class foo::defaults {
case $osfamily {
'Debian': {
$package_name = 'foo-ruby'
$service_name = 'foo'
$config_path = '/etc/foo/foo.conf'
$log_file = '/var/log/foo/foo.log'
$storage_path = '/var/lib/foo'
$conf_d_path = '/etc/foo/conf.d'
$plugin_path = '/usr/share/foo/plugins'
}
'Redhat': {
$package_name = 'ruby-foo'
$service_name = 'foo'
$config_path = '/etc/foo.conf'
$log_file = '/var/log/foo.log'
$storage_path = '/var/lib/foo'
$conf_d_path = '/etc/foo.d/'
$plugin_path = '/usr/lib/foo/plugins'
}
}
}
manifests/config.pp
class foo::config inherits foo {
file {
$config_path:
content => template('foo/foo.conf.erb'),
owner => 'root',
group => 'root',
mode => '0644';
$conf_d_path:
ensure => directory,
purge => true,
recurse => true,
owner => 'root',
group => 'root',
mode => '0755';
}
}
templates/foo.conf.erb
[main]
listen = <%= @listen %>
port = <%= @port %>
verbose = <%= @verbose_bool ? 'yes' : 'no' %>
log_file = <%= @log_file %>
storage_dir = <%= @storage_dir %>
@include <%= @conf_d_path %>/*.conf
manifests/plugin.pp
define foo::plugin (
$type,
$path = "${foo::defaults::plugin_path}/${name}.rb",
$verbose = undef,
) {
include foo::defaults
validate_absolute_path($path)
$verbose_bool = str2bool($verbose)
file { "${foo::defaults::conf_d_path}/${name}.conf":
content => template('foo/plugin.conf.erb'),
owner => 'root',
group => 'root',
mode => '0755',
notify => Class['foo::service'],
}
}
templates/plugin.conf.erb
include foo
foo::plugin {
'webapp':
type => 'passenger';
'db':
type => 'mysql',
verbose => true,
path => '/usr/local/share/custom_mysql.rb';
}
role/profile example
class profile::app {
include foo
foo::plugin {
'webapp':
type => 'passenger';
}
}
class profile::db {
include foo
foo::plugin {
'db':
type => 'mysql';
}
}
class role::server {
include profile::app
include profile::db
}
no conf.d?
manifests/config.pp
concat::fragment { 'foo_conf_header':
content => template('foo/foo.conf.erb'),
target => $config_path,
order => '00_header',
}
}
manifests/plugin.pp
define foo::plugin (
$type,
$path = "${foo::defaults::plugin_path}/$
{name}.rb",
$verbose = undef,
) {
include foo::defaults
validate_absolute_path($path)
$verbose_bool = str2bool($verbose)
concat { "foo_conf_plugin_${name}":
content => template('foo/plugin.conf.erb'),
target => $conf_file,
order => "10_plugin_${name}",
}
}
what about docs?
README.md
What it manages
Optional dependencies
describe 'foo' do
context 'default parameters' do
let (:params) {{ }}
it { should compile.with_all_deps }
it { should contain_class('foo::defaults') }
it { should contain_class('foo::install') }
it { should contain_class('foo::config') }
it { should contain_class('foo::service') }
end
end
spec/classes/foo_spec.rb
describe 'foo' do
let (:facts) {{
:osfamily => 'Redhat'
}}
context 'default parameters' do
it { should contain_package('ruby-foo') }
it { should contain_file('/etc/foo.conf').with(
:owner => 'root',
:group => 'root',
:mode => '0644',
)}
it { should contain_file('/etc/foo.d').with(
:owner => 'root',
:group => 'root',
:mode => '0755',
:purge => true,
:recurse => true,
)}
it { should contain_service('foo').with(
:ensure => 'running',
:enable => true,
)}
end
end
spec/classes/foo_spec.rb
describe 'foo' do
let (:facts) {{
:osfamily => 'Redhat'
}}
let (:config_file) { '/etc/foo.conf' }
context 'default parameters' do
it { should contain_file(config_file)
.with_content(/listen = 127\.0\.0\.1/)
}
it { should contain_file(config_file)
.with_content(/port = 1234/)
}
it { should contain_file(config_file)
.with_content(/verbose = no/)
}
it { should contain_file(config_file)
.with_content(%r{log_file = /var/log/foo.log})
}
it { should contain_file(config_file)
.with_content(%r{storage_dir = /var/lib/foo})
}
end
end
spec/classes/foo_spec.rb
describe 'foo' do
let (:facts) {{
:osfamily => 'Redhat'
}}
let (:config_file) { '/etc/foo.conf' }
Limit dependencies
anchor pattern
puppetlabs/ntp: https://forge.puppetlabs.com/puppetlabs/ntp
pdxcat/collectd: https://forge.puppetlabs.com/pdxcat/collectd
Library modules
puppetlabs/stdlib: https://forge.puppetlabs.com/puppetlabs/stdlib
puppetlabs/concat: https://forge.puppetlabs.com/puppetlabs/concat
Testing
rspec-puppet: http://rspec-puppet.com
beaker: https://github.com/puppetlabs/beaker
Thanks!
kitchen@kitchen.io
github.com/kitchen
twitter.com/kitchen
NationBuilder.com
(were hiring!)