Authors: Jeffrey McCune James Turnbull
Listing 8-6.
Placing a custom module into the Puppet module path
$ puppet-module build
========================================
Building /root/site-firewall for release
----------------------------------------
Done. Built: pkg/site-firewall-0.0.1.tar.gz
$ mv pkg/site-firewall-0.0.1 /etc/puppet/modules/firewall
The first command builds the module package and fills in the metadata for the module. The second command moves the built module into the puppet-module search path. We're now ready to try out the module and make sure the search path is working correctly. The operator usespuppet apply –e
, shown in
Listing 8-7
, to evaluate a single class declaration.
Listing 8-7.
Using the iptables module by loading the firewall class
$ puppet apply -e 'include firewall' --noop
notice: /Iptables[100 Puppet Prod]: rules would have changed... (noop) in 0.00 seconds
$ puppet apply -e 'include firewall'
Saving firewall rules to /etc/sysconfig/iptables: [ OK ]
notice: /Iptables[100 Puppet Prod]: rules have changed... in 0.39 seconds
Finally, in
Listing 8-8
, the operator verifies the rules are properly being managed using theiptables
command.
Listing 8-8.
Verifying that the iptables rules are being managed by Puppet
$ iptables -L INPUT -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0 0.0.0.0 tcp dpt:8140 /* 100 Puppet
Prod */
ACCEPT tcp -- 0.0.0.0 0.0.0.0 tcp dpt:8142 /* 101 Puppet Dev */
ACCEPT tcp -- 0.0.0.0 0.0.0.0 tcp dpt:8141 /* 101 Puppet
Test */
Using the iptables command, we're able to see Puppet is correctly using the iptables module to manage the host-based firewall. In the next section we'll learn how the operator uses the puppet-module tool to build his own Puppet modules.
The Puppet Forge is an excellent resource to download and re-use Puppet modules from the community. Modules you develop may also easily be published to the Forge. There are a number of benefits to publishing modules. First, people who use your modules may add functionality and help fix bugs, saving you time and effort. In addition, providing re-usable modules allows the Puppet community to focus on developing new functionality that could directly benefit you. Publishing your own modules also allows other Puppet users to save time and effort.
In this section, we'll see how the operator develops and publishes a small module to manage the NTP service on Debian and Redhat systems. It is important to keep in mind that modules published to the forge may be used on a wide variety of platforms. We'll learn how the operator uses conditionals in the Puppet manifests to clearly indicate when a particular platform is or is not supported.
As we learned in the previous section, thepuppet-module generate
command is useful for generating a skeleton module structure. This module structure is not directly usable by Puppet, and must first be built into a module package using thebuild
action. To get started, the Example.com operator generates the skeleton structure and adds the tree to a Git repository to track changes and history, as shown in
Listing 8-9
.
Listing 8-9.
Using puppet-module generate and git add
$ cd ~/src/modules/
$ puppet-module generate operator-ntp
=====================================================
Generating module at /root/src/modules/operator-ntp
-----------------------------------------------------
…
$ cd operator-ntp
$ git init
Initialized empty Git repository in .git/
$ git add .
$ git commit -a -m 'Initial commit'
Created initial commit fb7d7b2: Initial commit
17 files changed, 223 insertions(+), 0 deletions(-)
create mode 100644 Modulefile
create mode 100644 README
create mode 100644 files/README.markdown
create mode 100644 lib/puppet/facter/README.markdown
create mode 100644 lib/puppet/parser/functions/README.markdown
create mode 100644 lib/puppet/provider/README.markdown
create mode 100644 lib/puppet/type/README.markdown
create mode 100644 manifests/README.markdown
create mode 100644 manifests/init.pp
create mode 100644 metadata.json
create mode 100644 spec/README.markdown
create mode 100644 spec/spec.opts
create mode 100644 spec/spec_helper.rb
create mode 100644 spec/unit/puppet/provider/README.markdown
create mode 100644 spec/unit/puppet/type/README.markdown
create mode 100644 templates/README.markdown
create mode 100644 tests/init.pp
With the newly generated NTP module, the operator uses thegit init
,add
andcommit
actions to track the history of changes to the module. The module source code may then be published to the Internet usinghttp://github.com/
. Many module authors in the Puppet community publish their source code to github. Storing the module inside of a Git repository also allows the operator to track changes, tag releases, and quickly test out topic branches for new functionality.
The next step is to add functionality for a specific platform to the module. This module is designed to manage the NTP service and bind to a configurable set of upstream NTP servers. First, the operator adds support for Debian based systems. In
Listing 8-10
, he uses the new parameterized class feature of Puppet 2.6 to allow people using the module to specify the list of servers to synchronize against.
Listing 8-10.
Debian-specific functionality in the NTP module
$ vim manifests/init.pp
# Class: ntp
#
# This module manages the ntp service.
#
# Tested platforms:
# - Debian 6.0 Squeeze
#
# Parameters:
#
# $servers = [ "0.debian.pool.ntp.org iburst",
# "1.debian.pool.ntp.org iburst",
# "2.debian.pool.ntp.org iburst",
# "3.debian.pool.ntp.org iburst", ]
#
# Actions:
#
# Installs, configures, and manages the ntp service.
#
# Requires:
#
# Sample Usage:
#
# class { "ntp": servers => [ 'time.apple.com' ] }
#
# [Remember: No empty lines between comments and class definition]
class ntp($servers=[ "0.debian.pool.ntp.org iburst",
"1.debian.pool.ntp.org iburst",
"2.debian.pool.ntp.org iburst",
"3.debian.pool.ntp.org iburst",],
$ensure="running",
$autoupdate=false
) {
if ! ($ensure in [ "running", "stopped" ]) {
fail("ensure parameter must be running or stopped")
}
if $autoupdate == true {
$package_ensure = latest
} elsif $autoupdate == false {
$package_ensure = present
} else {
fail("autoupdate parameter must be true or false")
}
case $operatingsystem {
debian, ubuntu: {
$supported = true
$pkg_name = [ "ntp" ]
$svc_name = "ntp"
$config = "/etc/ntp.conf"
$config_tpl = "ntp.conf.debian.erb"
}
default: {
$supported = false
notify { "${module_name}_unsupported":
message => "The ${module_name} module is not supported on ${operatingsystem}",
}
}
}
if ($supported == true) {
package { $pkg_name:
ensure => $package_ensure,
}
file { $config:
ensure => file,
owner => 0,
group => 0,
mode => 0644,
content => template("${module_name}/${config_tpl}"),
require => Package[$pkg_name],
}
service { "ntp":
ensure => $ensure,
name => $svc_name,
hasstatus => true,
hasrestart => true,
subscribe => [ Package[$pkg_name], File[$config] ],
}
}
}
Once the main NTP class has been filled in, the operator builds the module using thepuppet-module build
command. The process shown in
Listing 8-11
fills in the metadata for the module and creates a module usable by Puppet. He then moves this module into the module search path at/etc/puppet/modules/ntp
to test the module. When building the module, make sure you are in the top level of the module directory structure containing theModulefile
file.
Listing 8-11.
Using the puppet-module build and install commands
$ puppet-module build
=======================================================
Building /root/src/modules/operator-ntp for release
-------------------------------------------------------
Done. Built: pkg/operator-ntp-0.0.1.tar.gz
$ cd /etc/puppet/modules
$ puppet-module install ~/src/modules/operator-ntp/pkg/operator-ntp-0.0.1.tar.gz
$ ln -s operator-ntp ntp
The operator first builds a new module package using thepuppet-module build
command. Once built, the operator changes directories to/etc/puppet/modules
to install the module. The Puppet autoloader will not find the module unless it is in the “ntp” directory, because the main class is named ntp. To address this problem, the operator simply creates a symbolic link from NTP to the forge module name. This will allow future versions to easily replace the existing version.
A NOTE ABOUT MODULE NAMES
If Puppet cannot find a module with a name exactly matching the module being created, the following errors may be encountered. While building the NTP module, puppet-module creates a module named operator-ntp. This module should be renamed when installing the module to ensure the autoloader properly loads the class.
# puppet apply --verbose -e 'class { ntp: ensure => stopped }'
Puppet::Parser::AST::Resource failed with error ArgumentError: Invalid resource type class at line 1 on node debian.puppetlabs.vm
# puppet apply --verbose -e 'include ntp'
info: Could not find class ntp for debian.puppetlabs.vm
Could not find class ntp at line 1 on node debian.puppetlabs.vm
Both of these errors may be corrected by symbolically linking /etc/puppet/modules/ntp to /etc/puppet/modules/operator-ntp after installing the module with puppet-module install.
It's now time to test out the newly developed module. To make sure the autoloader properly finds the NTP module, in
Listing 8-12
the operator executes a simplepuppet apply
command evaluating a single class declaration.
Listing 8-12.
Testing a new Puppet module with puppet apply on Debian
$ puppet apply --verbose -e 'class { ntp: ensure => running}'
info: Applying configuration version '1298492452'
notice: /Stage[main]/Ntp/Package[ntp]/ensure: ensure changed 'purged' to 'present'
info: /Stage[main]/Ntp/Package[ntp]: Scheduling refresh of Service[ntp]
info: FileBucket got a duplicate file /etc/ntp.conf ({md5}3e250ecaf470e1d3a2b68edd5de46bfd)
info: /Stage[main]/Ntp/File[/etc/ntp.conf]: Filebucketed /etc/ntp.conf to puppet with sum
3e250ecaf470e1d3a2b68edd5de46bfd
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/content: content changed
'{md5}3e250ecaf470e1d3a2b68edd5de46bfd' to '{md5}6e3461437c627101cf53e634abc62400'
info: /Stage[main]/Ntp/File[/etc/ntp.conf]: Scheduling refresh of Service[ntp]
notice: /Stage[main]/Ntp/Service[ntp]: Triggered 'refresh' from 2 events
$ puppet apply --verbose -e 'class { ntp: ensure => running }'
info: Applying configuration version '1298492574'