Sad to say, but not many modules include good tests. Good tests help keep embarrassing bugs from going out. Tests can save you a lot of time, avoiding the exhaustive debugging of an issue in production that turns out to be a wrong type used, or a similar mistake.
This chapter will teach you how to add good tests to your modules. When I got started, I struggled a lot due to a lack of good examples. This chapter provides good examples of each type of test you should be doing. It’s my intention that you’d be able to use the examples provided here like tinker toys, and build a good set of tests for your modules without much effort.
This chapter won’t provide exhaustive documentation of rspec
or beaker
, the testing tools of choice for the Puppet ecosystem. However, you should be able to build a good foundation of tests from what we cover in this chapter.
Let’s get started by setting up your testing tools.
The first time you set up to do testing, you’ll need to install some specific software.
You can use the Ruby version that comes with your operating system. If you are using the Vagrant testing setup documented in this book, it is easy to install Ruby into the system packages:
[
vagrant@client
~
]
$
sudo
yum
install
-y
ruby-devel
rubygems
rake
libxml2-devel
Guides for installing Ruby can be found in Appendix C.
Install the bundler
gem for local installation of necessary dependencies:
$
sudo
gem
install
bundler
--no-ri
--no-rdoc
Fetching:
bundler-1.11.1.gem
(
100%
)
Successfully
installed
bundler-1.11.1
1
gem
installed
Add the following lines to the Gemfile in the module directory:
gem
'beaker-rspec'
,
:require
=>
false
gem
'pry'
,
:require
=>
false
These lines will ensure that Beaker is installed along with the other dependencies in the next step. Beaker gem dependencies require these development libraries to compile binary extensions:
[
vagrant@client
~
]
$
sudo
yum
install
-y
gcc-d++
libxml2-devel
libxslt-devel
If you haven’t done this already, you’ll need to install the puppetlabs_spec_helper and other dependency gems. The best way to do this is to run the bundler install
command within the module directory. bundle
will read the Gemfile and pull in rspec
, rspec-puppet
, beaker
, and all of their dependencies. These are testing and template creation tools that simplify test creation:
[
vagrant@client
puppet
]
$
bundler
install
Fetching
gem
metadata
from
https://rubygems.org/..........
Fetching
version
metadata
from
https://rubygems.org/..
Resolving
dependencies...
Installing
rake
11.1.1
Installing
CFPropertyList
2.2.8
Using
diff-lcs
1.2.5
...snip
a
long
list
of
gems...
Installing
rspec
3.4.0
Installing
puppet
4.10.9
Installing
rspec-puppet
2.3.2
Installing
puppetlabs_spec_helper
1.1.1
Installing
beaker
2.37.0
Installing
beaker-rspec
5.3.0
Bundle
complete
!
6
Gemfile
dependencies,
95
gems
now
installed.
Use
`
bundle
show
[
gemname
]
`
to
see
where
a
bundled
gem
is
installed.
sudo
when running bundler
. Its purpose is to vendor the gems locally in ~/.gems/, without affecting the system gems.The next step is to set up your module for testing. We’ll have to modify a few files to use the best tools for this.
Create a .fixtures.yml file to define the testing fixtures (dependencies) and where to acquire them for testing purposes. The information in this file should duplicate the dependencies in metadata.json.
The top of the file should always be the same. This tells the testing frame to copy the current module from its directory:
fixtures
:
symlinks
:
puppet
:
"
#{
source_dir
}
"
Then define each dependency for your module and the minimum version you support. You can list their names on the Puppet Forge or their GitHub URL. The following two examples will have similar effects. From the Forge:
forge_modules
:
stdlib
:
repo
:
"puppetlabs/stdlib"
ref
:
4
.
5
.
1
From GitHub:
repositories
:
stdlib
:
repo
:
"git://github.com/puppetlabs/puppetlabs-stdlib"
ref
:
"4.5.1"
If you are testing development of multiple modules, you may want to use symlinks to the source tree for each. Assuming the dependency is in the same directory structure:
symlinks
:
some_dependency
:
"
#{
source_dir
}
/../
some_dependency
"
Test that dependency setup worked properly like so:
$
rake
spec
(
in
/
etc
/
puppetlabs
/
code
/
environments
/
test
/
modules
/
puppet
)
Notice
:
Preparing
to
install
into
/
etc
/
puppetlabs
/
code
/
environments
/
test
/
modules
/
puppet
/
spec
/
fixtures
/
modules
.
.
.
Notice
:
Downloading
from
https
:
/
/
forgeapi
.
puppetlabs
.
com
.
.
.
Notice
:
Installing
-
-
do
not
interrupt
.
.
.
/
etc
/
puppetlabs
/
code
/
environments
/
test
/
modules
/
puppet
/
spec
/
fixtures
/
modules
└
─
─
puppetlabs
-
stdlib
(
v3
.
2
.
1
)
/
usr
/
bin
/
ruby
-
I
/
usr
/
lib
/
ruby
/
gems
/
1
.
8
/
gems
/
rspec
-
support
-
3
.
2
.
2
/
lib
:
.
.
.
No
examples
found
.
Finished
in
0
.
00027
seconds
(
files
took
0
.
04311
seconds
to
load
)
0
examples
,
0
failures
This shows that all fixtures (dependencies) were installed, but no examples (tests) were available. Let’s start building one now.
Now let’s build some tests for the module. We know, few people think that building tests is fun work—but it is important work that will save you time and effort down the road.
You should follow these guidelines for creating useful tests:
Let’s look at some examples that test each one of these situations.
Within your module directory, change into the spec/classes/ directory. Inside this directory, create a file named <modulename>_spec.rb.
[vagrant@client puppet]$ cd spec/classes [vagrant@client classes]$ $EDITOR puppet_spec.rb
First, we will define a test where the module test builds (compiles) a catalog successfully with the default options:
require
'spec_helper'
describe
'
puppet
'
,
:type
=
>
'class'
do
context
'with defaults for all parameters'
do
it
{
is_expected
.
to
contain_class
(
'puppet'
)
}
it
{
is_expected
.
to
contain_class
(
'puppet::params'
)
}
it
{
is_expected
.
to
compile
.
with_all_deps
}
end
end
Let’s go ahead and run the testing suite against this very basic test:
[
vagrant@client
puppet
]
$
rake
spec
(
in
/etc/puppetlabs/code/modules/puppet
)
ruby
-I/opt/puppetlabs/puppet/lib/ruby/gems/2.1.0/gems/rspec-support-3.2.2/lib:
..
Finished
in
10.22
seconds
(
files
took
0.56629
seconds
to
load
)
2
examples,
0
failures
Now that our basic test passed, let’s go on to start checking the input parameters.
What if we expand the tests to include every possible input value? Rather than repeating each test with a different value, we use Ruby loops to iteratively build each of the tests from an array of values:
[
'1'
,
'0'
].
each
do
|
repo_enabled
|
[
'emerg'
,
'crit'
,
'alert'
,
'err'
,
'warning'
,
'notice'
,
'info'
].
each
do
|
loglevel
|
context
"with repo_enabled =
#{
repo_enabled
}
, loglevel
#{
loglevel
}
"
do
let
:params
do
{
:repo_enabled
=>
repo_enabled
,
:loglevel
=>
loglevel
,
}
end
it
{
is_expected
.
to
contain_package
(
'puppet-agent'
)
.
with
({
'version'
=>
'1.10.9-1'
})
}
end
end
end
Whoa, look at that. We added 15 lines of code and yet it’s performing 36 more tests:
[
vagrant@client
puppet
]
$
rake
spec
(
in
/etc/puppetlabs/code/modules/puppet
)
ruby
-I/opt/puppetlabs/puppet/lib/ruby/gems/2.1.0/gems/rspec-support-3.2.2/lib:
......................................
Finished
in
10.53
seconds
(
files
took
0.56829
seconds
to
load
)
38
examples,
0
failures
Always test to ensure incorrect values fail. This example shows two tests that are intended to fail:
context
'with invalid loglevel'
do
let
:params
do
{
:loglevel
=>
'annoying'
}
end
it
{
is_expected
.
to
compile
.
with_all_deps
}
end
context
'with invalid repo_enabled'
do
let
:params
do
{
:repo_enabled
=>
'EPEL'
}
end
it
{
is_expected
.
to
compile
.
with_all_deps
}
end
Run the tests to see what error messages are kicked back:
[
vagrant@client
puppet
]
$
rake
spec
Failures:
1
)
puppet4
with
invalid
loglevel
should
build
a
catalog
w/o
dependency
cycles
Failure/Error:
should
compile.with_all_deps
error
during
compilation:
Parameter
loglevel
failed
on
Class
[
Puppet
]
:
Invalid
value
"annoying"
.
Valid
values
are
debug,
info,
notice,
warning,
err,
alert,
emerg,
crit,
verbose.
at
line
1
# ./spec/classes/puppet_spec.rb:52:in 'block (3 levels) in <top (required)>'
2
)
puppet4
with
invalid
repo_enabled
should
build
a
catalog
w/o
dependency
cycles
Failure/Error:
should
compile.with_all_deps
error
during
compilation:
Expected
parameter
'repo_enabled'
of
'Class[Puppet]'
to
have
type
Enum
[
'0'
,
'1'
]
,
got
String
at
line
1
# ./spec/classes/puppet_spec.rb:65:in 'block (3 levels) in <top (required)>'
Finished
in
10.81
seconds
(
files
took
0.57989
seconds
to
load
)
40
examples,
2
failures
Now let’s change the expect
lines to accept the error we expect:
it
do
is_expected
.
to
compile
.
with_all_deps
.
and_raise_error
(
Puppet
:
:Error
,
/Invalid value "annoying". Valid values are/
)
end
and the following for the test of repo_enabled
:
it
do
is_expected
.
to
compile
.
with_all_deps
.
and_raise_error
(
Puppet
:
:Error
,
/Expected parameter 'repo_enabled' .* to have type Enum/
)
)
end
Now when you run the tests, you will see the tests were successful because the invalid input produced the expected error.
You can test to ensure that a file resource is created. The simplest form is:
it
{
is_expected
.
to
contain_file
(
'/etc/puppetlabs/puppet/puppet.conf'
)
}
Now, this only checks that the file exists, and not that it was modified correctly by the module. Test resource attributes using this longer form:
it
do
is_expected
.
to
contain_file
(
'/etc/puppetlabs/puppet/puppet.conf'
)
.
with
({
'ensure'
=>
'present'
,
'owner'
=>
'root'
,
'group'
=>
'root'
,
'mode'
=>
'0444'
,
})
end
Finally, you can also check the content against a regular expression. Here’s an example where we pass in a parameter, and then want to ensure it would be written to the file:
let
:params
do
{
:loglevel
=>
'notice'
,
}
end
it
do
is_expected
.
to
contain_file
(
'/etc/puppetlabs/puppet/puppet.conf'
)
.
with_content
({
/^s*loglevels*=s*notice/
})
end
Some manifests or tests may require that certain facts are defined properly. Inside the context
block, define a hash containing the fact values you want to have available in the test:
let
:facts
do
{
:osfamily
=>
'RedHat'
,
:os
=>
{
'family'
=>
'RedHat'
,
'release'
=>
{
'major'
=>
'7'
,
'minor'
=>
'2'
}
},
}
end
By default, the hostname
, domain
, and fqdn
facts are set from the fully qualified domain name of the host. To adjust the node name and these three facts for testing purposes, add this to the test:
let
(
:node
)
{
'webserver01.example.com'
}
Within your module directory, change to the spec/fixtures/ directory. Inside this directory, create a subdirectory named hiera, containing a valid hiera.yaml file for testing:
[
vagrant@client
puppet
]
$
cd
spec/fixtures
[
vagrant@client
puppet
]
$
mkdir
hiera
[
vagrant@client
classes
]
$
$EDITOR
hiera/hiera.yaml
You can change anything you want that is valid for Hiera in this configuration file, except for the datadir
, which should reside within the fixtures path. Unless you desire a specific change, the following file could be used unchanged in every module:
# spec/fixtures/hiera/hiera.yaml
---
:backends
:
-
yaml
:yaml
:
:datadir
:
/etc/
puppetlabs
/
code
/
hieradata
:hierarchy
:
-
os
/
"%{facts.os.family}"
-
common
Now, add the following lines to a test context within one of the class spec files:
let
(
:hiera_config
)
{
'spec/fixtures/hiera/hiera.yaml'
}
hiera
=
Hiera
.
new
(
:config
=>
'spec/fixtures/hiera/hiera.yaml'
)
Now create your Hiera input files. The only necessary file is spec/fixtures/hiera/common.yaml. The others can be added only when you want to test things:
---
puppet
:
:loglevel
:
'notice'
puppet
:
:repo_enabled
:
'1'
puppet
:
:agent
::
status
:
'running'
puppet
:
:agent
::
enabled
:
true
You can use this Hiera data to configure the tests:
let
:params
do
{
:repo_enabled
=>
hiera
.
lookup
(
'puppet::repo_enabled'
,
nil
,
nil
),
:loglevel
=>
hiera
.
lookup
(
'puppet::loglevel'
,
nil
,
nil
),
}
end
This configuration allows you to easily test the common method of using Hiera to supply input parameters for your modules.
In some situations, your module will depend upon a class that requires some parameters to be provided. You cannot set parameters or use Hiera for that class, because it is out of scope for the current class and test file.
The workaround is to use a pre_condition
block to call the parent class in resource-style format. Pass the necessary parameters for testing as parameters for the resource declaration, and this module instance will be created before your module is tested.
Here is an example from my mcollective
module, which had to solve this exact problem:
describe
'mcollective::client'
do
let
(
:pre_condition
)
do
'class { "mcollective": hosts => ["middleware.example.net"], client_password => "fakeTestingClientPassword", server_password => "fakeTestingServerPassword", psk_key => "fakeTestingPreSharedKey", }'
end
.
.
.
tests
for
the
mcollective
:
:client
class
.
.
.
Unit tests should be created for any functions added by the module. Each function test should exist in a separate file, stored in the spec/functions/ directory of the module, and named for the function followed by the _spec.rb extension.
At a bare minimum, the test should ensure that:
Here is an example that should work for our make_boolean()
example:
#! /usr/bin/env ruby -S rspec
require
'spec_helper'
describe
'make_boolean'
do
describe
'should raise a ParseError if there is less than 1 argument'
do
it
{
is_expected
.
to
run
.
with_params
()
.
and_raise_error
(
Puppet
:
:ParseError
)
}
end
describe
"should convert string '0' to false"
do
it
{
is_expected
.
to
run
.
with_params
(
"0"
)
.
and_return
(
false
)
}
end
end
Within the spec/classes/ directory, create a file named agent_spec.rb. This is an exercise for you. Build the agent class, testing every valid and invalid input just like we did for the Puppet class.
For this, we simply want to test that the package, config file, and service resources are all defined:
require
'spec_helper'
describe
'
puppet::agent
'
,
:type
=
>
'class'
do
context
'with defaults for all parameters'
do
it
do
is_expected
.
to
contain_package
(
'puppet-agent'
)
.
with
(
{
'version'
=
>
'latest'
}
)
is_expected
.
to
contain_file
(
'puppet.conf'
)
.
with
(
{
'ensure'
=
>
'file'
}
)
is_expected
.
to
contain_service
(
'puppet'
)
.
with
(
{
'ensure'
=
>
'running'
}
)
end
it
{
is_expected
.
to
compile
.
with_all_deps
}
end
end
We have demonstrated how to build tests. Now, you should build out more tests for valid and invalid input.
Every object type provided by a module can be tested. Place tests for the other types in the directories specified in Table 17-1.
Type | Directory |
---|---|
Class | spec/classes/ |
Defined resource type | spec/defines/ |
Functions | spec/functions/ |
Node differences | spec/hosts/ |
The rspec unit tests discussed earlier in this chapter validate individual features by testing the code operation in the development environment. Rspec tests provide low-cost, easy-to-implement code validation.
In contrast, the Beaker test harness spins up (virtual) machines to run platform-specific acceptance tests against Puppet modules.
Beaker creates a set of test nodes (or nodeset) running each operating system and configuration you’d like to perform system tests for. Beaker tests are significantly slower and more resource-intensive than rspec tests, but they provide a realistic environment test that goes far beyond basic code testing.
As Beaker will need to run vagrant
commands to create virtual machines to run the test suites on, you’ll need to run Beaker on your development system rather than one of the virtual machines you’ve been using so far. Following are the steps to set up and appropriate testing environment on your system.
gem install bundler
command to install Bundler.bundle install
to install the test dependencies.Create a directory within the module to contain the nodesets used for testing:
[
vagrant@client
puppet
]
$
mkdir
-p
spec/acceptance/nodesets
Create a file name default.yml within this directory. This file should contain YAML data for nodes to be created for the test. The following example will create a test node using the same Vagrant box utilized within this book.
HOSTS
:
centos
-
7
-
x64
:
roles
:
-
agent
platform
:
el
-
7
-
x86_64
box
:
puppetlabs
/
centos
-
7
.
2
-
64
-
nocm
hypervisor
:
vagrant
vagrant_memsize
:
1024
Create additional nodeset files within this directory for all platforms that should be tested. Sample nodeset files for different operating systems can be found at Example Vagrant Hosts Files.
Create a file named spec/spec_helper_acceptance.rb within the module directory. This file defines the tasks needed to prepare the test system.
The following example will ensure Puppet and the module dependencies are installed:
require
'beaker-rspec'
require
'pry'
step
"
Install Puppet on each host
"
install_puppet_agent_on
(
hosts
,
{
:puppet_collection
=
>
'pc1'
}
)
RSpec
.
configure
do
|
c
|
# Find the module directory
module_root
=
File
.
expand_path
(
File
.
join
(
File
.
dirname
(
__FILE__
)
,
'..'
)
)
# Enable test descriptions
c
.
formatter
=
:documentation
# Configure all nodes in nodeset
c
.
before
:suite
do
# Install module and dependencies
puppet_module_install
(
:source
=
>
module_root
,
:module_name
=
>
'
puppet
'
,
)
hosts
.
each
do
|
host
|
# Install dependency modules
on
host
,
puppet
(
'module'
,
'install'
,
'puppetlabs-stdlib'
)
,
{
:acceptable_exit_codes
=
>
[
0
,
1
]
}
on
host
,
puppet
(
'module'
,
'install'
,
'puppetlabs-inifile'
)
,
{
:acceptable_exit_codes
=
>
[
0
,
1
]
}
end
end
end
This is generally a consistent formula you can use with any module. The adjustments specific to this module have been bolded in the preceding example.
The tests are written in rspec, like the unit tests created in the previous section. However ServerSpec tests are also available and can be used in combination with rspec tests.
For most modules that set up services, the first test should be an installation test to validate that it installs with the default options, and that the service is properly configured.
Create a file named spec/acceptance/installation_spec.rb within the module directory. The following example defines some basic tests to ensure that the package is installed and that the service runs without returning an error:
require
'spec_helper_acceptance'
describe
'
puppet
class'
do
context
'default parameters'
do
# Using puppet_apply as a helper
it
'should install with no errors using default values'
do
puppetagent
=
<
<
-
EOS
class
{
'
puppet::agent
'
:
}
EOS
# Run twice to test idempotency
expect
(
apply_manifest
(
puppetagent
)
.
exit_code
)
.
to_not
eq
(
1
)
expect
(
apply_manifest
(
puppetagent
)
.
exit_code
)
.
to
eq
(
0
)
end
describe
package
(
'puppet-agent'
)
do
it
{
should
be_installed
}
end
describe
file
(
'
/etc/puppetlabs/puppet/puppet.conf
'
)
do
it
{
should
be_a_file
}
end
describe
service
(
'puppet'
)
do
it
{
should
be_enabled
}
end
end
end
You can and should create more extensive tests that utilize different input scenarios for the module. Each test should be defined as a separate file in the spec/acceptance/ directory, with a filename ending in _spec.rb.
You can find more information, including details about writing good tests, in these places:
rspec.info
site.serverspec.org
site.To spin up virtual machines to run the test, you’ll need to run the acceptance tests from a system that has Vagrant installed. Your personal system will work perfectly fine for this purpose.
Let’s go ahead and run the entire acceptance test suite:
$
bundle
exec
rspec
spec/acceptance
Hypervisor
for
centos-7-x64
is
vagrant
Beaker::Hypervisor,
found
some
vagrant
boxes
to
create
=
=
>
centos-7-x64:
Forcing
shutdown
of
VM...
=
=
>
centos-7-x64:
Destroying
VM
and
associated
drives...
created
Vagrantfile
for
VagrantHost
centos-7-x64
Bringing
machine
'centos-7-x64'
up
with
'virtualbox'
provider...
=
=
>
centos-7-x64:
Importing
base
box
'puppetlabs/centos-7.2-64-nocm'
...
Progress:
100%
=
=
>
centos-7-x64:
Matching
MAC
address
for
NAT
networking...
The test will spin up a Vagrant instance for each node specified in the spec/acceptance/nodeset/ directory. It will run the tests on each of them, and output the status to you, as shown here:
puppet::agent class default parameters should install with no errors using default values Package"puppet-agent"
should be installed File"/etc/puppetlabs/puppet/puppet.conf"
should be a file Service"puppet"
should be enabled Destroying vagrantboxes
==
> centos-7-x64: Forcing shutdown of VM...==
> centos-7-x64: Destroying VM and associated drives... Finished in 17.62 seconds(
files took1
minute 2.88 seconds to load)
4
examples,0
failures
As shown, all tests have completed without any errors.
Test failure messages are clear and easy to read. However, they won’t necessarily tell you why something isn’t true. When testing this example, I got the following errors back:
2)
puppet::agent class default parameters Package"puppet-agent"
should be installed Failure/Error: it{
should be_installed}
expected Package"puppet-agent"
to be installed# ./spec/acceptance/installation_spec.rb:17:in `block (4 levels) in <top>'
Beaker provides extensive debug output, showing every command and its output on the test nodes. Preserve the test system for evaluation by setting the BEAKER_destroy
environment variable to no
:
$
BEAKER_destroy
=
no
BEAKER_debug
=
yes
bundle
exec
rspec
spec/acceptance
You can isolate debugging to a single host configuration. For example, if there were a nodeset in spec/acceptance/nodeset/centos-6-x86.yml, then the following command would run the tests in debug mode on the CentOS 6 node only:
$ BEAKER_set=centos-6-x86 BEAKER_debug=yes rspec spec/acceptance
Debug output will show the complete output of every command run by Beaker on the node, like so:
* Install Puppet on each host centos-7-x64 01:36:36$
rpm --replacepkgs -ivh Retrieving http://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm Preparing...########################################
Updating / installing... puppetlabs-release-7-11########################################
warning: rpm-tmp.KNaXPi: Header V4 RSA/SHA1 Signature, key ID 4bd6ec30: NOKEY centos-7-x64 executed in 0.35 seconds
Avoid reprovisioning the node each time when you are debugging code. The following process is a well-known debugging pattern:
Run the test but keep the node around:
$
BEAKER_destroy
=
no
BEAKER_debug
=
yes
rspec
spec
/
acceptance
Rerun the test using the existing node:
$
BEAKER_destroy
=
no
BEAKER_provision
=
no
BEAKER_debug
=
yes
bundle
exec
rspec
spec
/
acceptance
Run the test with a fresh provisioning:
$
rspec
spec
/
acceptance
When all else fails, access the console of a host so that you can dig around and determine what happened during the test. At the point where investigation is necessary, add binding.pry
to the _spec.rb test file that is failing:
describe
package
(
'puppet-agent'
)
do
it
{
should
be_installed
}
end
# Access host console to debug failure
binding.pry
Then rerun the test:
$
BEAKER_destroy
=
no
bundle
exec
rspec
spec/acceptance
...
From:
learning-puppet4/puppet4/spec/acceptance/installation_spec.rb
@
line
20
:
16:
describe
package
(
'puppet-agent'
)
do
17:
it
{
should
be_installed
}
18:
end
19:
# Make the debug console available
=
>
20:
binding.pry
21:
[
1
]
pry
(
RSpec::ExampleGroups::PuppetAgentClass::DefaultParameters
)
>
Pry is a debugging shell for Ruby, similar but more powerful than IRB. Documenting all features is beyond the scope of this book, but the following commands have proven very useful to investigate host state.
You can cat
and edit
local files directly:
>>
cat
spec/acceptance/installation_spec.rb
>>
edit
spec/acceptance/installation_spec.rb
Enter shell-mode
to be placed in a local directory. Prefix commands with a period to execute them on the local system:
>>
shell-mode
learning-puppet4/puppet4
$
.ls
spec/acceptance/
installation_spec.rb
nodesets/
Get a list of hosts in the text, with their index number:
>>
hosts.each_with_index
do
|
host,i
|
"#{i} #{host.hostname()} "
;
end
;
1
centos-7-x64
Use the host index to execute a command on the host. Add a trailing semicolon to avoid debugger verbosity:
>>
on
hosts
[
0
]
,
'rpm -qa |grep puppet'
;
centos-7-x64
02:50:14
$
rpm
-qa
|
grep
puppet
puppet-3.8.4-1.el7.noarch
puppetlabs-release-7-11.noarch
Whoops, the wrong version of Puppet was installed. The default is still currently to install the old open source version (Puppet v3). Changing the type
parameter to aio
in the nodeset solved this problem.
For more information about using this debugger, refer to Pry: Get to the Code.
Beaker can test dozens of other resources, including network interfaces, system devices, logical configurations, and TLS certificates. A complete list of resource types with should
and expect
examples can be found at ServerSpec Resource Types.
Beaker is a fast-moving project with new features added constantly. Check the Beaker GitHub project for the latest documentation.
There are a number of Puppet module skeletons that preinstall frameworks for enhanced testing above and beyond what we’ve covered. You may want to tune the module skeleton you use to include testing frameworks and datasets consistent with your release process.
Place the revised skeleton in the ~/.puppetlabs/opt/puppet/cache/puppet-module/skeleton directory, or specify it on the puppet module generate
command line with --module_skeleton_dir=path/to/skeleton
.
The following are skeletons I have found useful at one time or another (in their own words):
This skeleton is very opinionated. It’s going to assume you’re going to start out with tests (both unit and system), that you care about the Puppet style guide, test using Travis, keep track of releases and structure your modules according to strong conventions.
puppet-module-skeleton README
This skeleton is popular and recommended by Puppet Labs Professional Services Engineers.
The module comes with everything you need to develop infrastructure code with Puppet and feel confident about it.
puppet-skeleton README
This skeleton includes helpers to spin up Vagrant instances and run tests on them.
This is a skeleton project for Web Operations teams using Puppet. It ties together a suite of sensible defaults, best current practices, and re-usable code. The intentions of which are two-fold:
New projects can get started and bootstrapped faster without needing to collate or rewrite this material themselves.
The standardization and modularization of these materials makes it easier for ongoing improvements to be shared, in both directions, between different teams.
puppet-skeleton README
A complete working solution with:
Puppet master and agent nodes on Puppet Open Source
Spotify Puppet Explorer and PuppetDB
Hiera configuration
Dynamic Git environments by r10k
External puppet modules installation and maintenance by r10k
Landrush local DNS
Couple of bootstrap Puppet classes:
common::filebucket
- use of filebucket on all files
common::packages
- central packages installation from hiera
common::prompt
- a Bash command prompt with support for Git and Mercurialpuppet-os-skeleton README
You can find many others by searching for “puppet skeleton” on GitHub. In particular, you can find skeletons specialized for specific application frameworks: OpenStack, Rails, Django, and so on.
You may have found a tricky problem not covered by the examples here. At this point, it is best to refer to the original vendor documentation:
Much of this documentation is dated, but still valid. There are open bugs to provide Puppet 4.x-specific documentation, and I will update this section as soon as it is available.
Each class and defined type should have both unit and acceptance tests defined for it. Some good rules of thumb for tests are:
In this chapter, we have discussed how to test modules for:
This has covered the necessary tests that should be included in every module. In the next chapter, we’re going to look at how to publish modules on the Puppet Forge.