Authors: Jeffrey McCune James Turnbull
In this example, thegem
command installs executables to/var/lib/gems/1.8/gems
. The operator adds this file system location to thePATH
variable, as shown in
Listing 8-36
, to complete the installation of cucumber-puppet.
Listing 8-36.
Adding the gem executable directory to the PATH
# export PATH="/var/lib/gems/1.8/bin:$PATH"
# which cucumber-puppet
/var/lib/gems/1.8/bin/cucumber-puppet
Once the cucumber-puppet command is available, we may proceed with writing a story describing the desired Puppet catalog behavior.
The behavior of Puppet is described in Cucumber “stories.” Before writing a story describing the catalog behavior and features, the cucumber-puppet testing directory needs to be created. On a testing system, basic example step definitions should be installed using the cucumber-puppet-gen command (see
Listing 8-37
).
Listing 8-37.
Installing basic step defintions with cucumber-puppet-gen
# cd /etc/puppet
# cucumber-puppet-gen world
Generating with world generator:
[ADDED] features/support/world.rb
[ADDED] features/steps
[ADDED] features/support/hooks.rb
Once the basic steps have been installed, the Example.com operator configures cucumber-puppet by modifying thehooks.rb
file. Before doing so, he adds and commits the new files to the Git repository as shown in
Listing 8-38
.
Listing 8-38.
Adding cucumber-puppet steps to Git
# cd /etc/puppet
# git add features
# git commit -m 'Add cucumber-puppet generated steps'
Once added to Git, the operator modifies thehooks.rb
file to configure cucumber-puppet, as shown in
Listing 8-39
.
Listing 8-39.
Changes to hooks.rb to configure cucumber-puppet
# git diff
diff --git a/features/support/hooks.rb b/features/support/hooks.rb
index 77db992..3588300 100644
--- a/features/support/hooks.rb
+++ b/features/support/hooks.rb
@@ -1,7 +1,9 @@
Before do
# local configuration
# @confdir = File.join(File.dirname(__FILE__), '..', '..')
+ @confdir = "/etc/puppet"
# @manifest = File.join(@confdir, 'manifests', 'site.pp')
+ @manifest = "/etc/puppet/manifests/site.pp"
# adjust facts like this
@facts['architecture'] = "i386"
End
As you can see, the operator added two lines to thehooks.rb
file. First, the configuration directory for Puppet is set to/etc/puppet
. This corresponds to theconfdir
configuration setting. Next, the mainsite.pp
file is configured using the @manifest variable. This setting should point to the full path of thesite.pp
file,/etc/puppet/manifests/site.pp
by default.
Once cucumber-puppet is configured, the catalog policy in
Listing 8-40
is used to test and verify the behavior of the catalog. The Example.com operator uses the cucumber-puppet-gen command to generate a template catalog policy file.
Listing 8-40.
The initial web server cucumber-puppet policy.feature file
# cd /etc/puppet/
# cucumber-puppet-gen policy
Generating with policy generator:
[ADDED] features/catalog
# git add features/catalog/
# git commit -m 'Add initial catalog policy template'
[master a0c6c3c] Add initial catalog policy template
1 files changed, 14 insertions(+), 0 deletions(-)
create mode 100644 features/catalog/policy.feature
With these three commands, the operator generates a new template for the catalog policy, adds the policy to the Git index, and then commits the new file to the repository. The cucumber-puppet policies closely resemble the natural language stories of Cucumber, as shown in
Listing 8-41
.
Listing 8-41.
A template cucumber-puppet policy
# cat /etc/puppet/features/catalog/policy.feature
Feature: General policy for all catalogs
In order to ensure applicability of a host's catalog
As a manifest developer
I want all catalogs to obey some general rules
Scenario Outline: Compile and verify catalog
Given a node specified by "features/yaml/.example.com.yaml"
When I compile its catalog
Then compilation should succeed
And all resource dependencies should resolve
Examples:
| hostname |
| localhost |
There are a few key sections of the policy file. First, the Scenario section is tested for every node listed in the Examples section. Cucumber-puppet substitutes the name listed underneath thehostname
header into the filename listed in theGiven a node specified by
section. This node cache contains a list of top-level parameters set bysite.pp
, the ENC, and Facter. Using the cached node information stored on the Puppet master allows cucumber-puppet to effectively simulate a catalog request from each Puppet agent.
In order to populate these node definition files, the Example.com operator copies the cached node files from the Puppet Master as shown in
Listing 8-42
. These node files are located in$yamldir /node/,
where$yamldir
is a configuration setting on the Master system. Let's see how the operator provides this information to cucumber-puppet.
Listing 8-42.
Copying node YAML files from the Puppet Master into cucumber-puppet
# cd /etc/puppet
# mkdir /etc/puppet/features/yaml
# puppet master --configprint yamldir
/var/lib/puppet/yaml
# cp /var/lib/puppet/yaml/node/{www,mail}.example.com.yaml \
/etc/puppet/features/yaml/
First, the operator changes to the Puppet configuration directory, then creates the /etc/puppet/features/yaml directory. He then determines where the Puppet Master caches nodes compiled by the Puppet Master using the--configprint yamldir
option. With this information, he copies the cached node information for the mail and web server into the cucumber-puppet directory structure.
With the node information in place, he modifies the catalog policy slightly to test catalog compilation for the mail and web server, replacing thelocalhost
entry in the template. This modification is shown in
Listing 8-43
.
Listing 8-43.
Add www and mail to the cucumber-puppet catalog policy
# git diff
diff --git a/features/catalog/policy.feature b/features/catalog/policy.feature
index c742189..1ea545e 100644
--- a/features/catalog/policy.feature
+++ b/features/catalog/policy.feature
@@ -11,4 +11,5 @@ Feature: General policy for all catalogs
Examples:
| hostname |
- | localhost |
+ | www |
+ | mail |
We can see that the operator replaces thelocalhost
entry with two additional entries forwww
andmail
. These names will be substituted into the file path when cucumber-puppet loads the node information. They also match the two YAML node files copied from the Puppet master yaml directory.
With this information in place, the operator commits the changes using the commands in
Listing 8-44
and is ready to start testing changes to the Puppet manifests.
Listing 8-44.
Committing node information and catalog policy to the git repository
$ git status
#On branch master
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# new file: features/yaml/mail.example.com.yaml
# new file: features/yaml/www.example.com.yaml
#
# Changed but not updated:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: features/catalog/policy.feature
#
# git commit -m 'Add mail and web node YAML, update catalog policy'
[master c71e527] Add mail and web node YAML, update catalog policy
3 files changed, 141 insertions(+), 1 deletions(-)
create mode 100644 features/yaml/mail.example.com.yaml
create mode 100644 features/yaml/www.example.com.yaml
With the YAML node caches copied into place and the catalog policy updated, the operator simply executes cucumber-puppet as shown in
Listing 8-45
, testing his current manifests.
Listing 8-45.
Testing manifests with cucumber-puppet
$ cucumber-puppet features/catalog/policy.feature
Feature: General policy for all catalogs
In order to ensure applicability of a host's catalog
As a manifest developer
I want all catalogs to obey some general rules
Scenario Outline: Compile and verify catalog
Given a node specified by "features/yaml/.example.com.yaml"
When I compile its catalog
Then compilation should succeed
And all resource dependencies should resolve
Examples:
| hostname |
| www |
| mail |
2 scenarios (2 passed)
8 steps (8 passed)
0m0.104s
While in the/etc/puppet
directory, the operator executes cucumber-puppet with the path to the catalog policy file. Cucumber-puppet then tests both the mail and web server and validates that the catalog compilation succeeds. The operator expects as much, but a basicsite.pp
file is being used with an empty node declaration ofnode default { }
.
To verify that cucumber-puppet will properly report catalog failures, he modifies the site.pp in
Listing 8-46
to contain the following node definitions:
Listing 8-46.
/etc/puppet/manifests/site.pp test failure with cucumber-puppet
# /etc/puppet/manifests/site.pp
node default {
notify { "unclassified":
message => "This node is not classified",
}
}
node www {
notify { "web":
message => "This node is the web server.",
}
}
node mail {
notify { "mail":
message => "This node is the mail server.",
}
fail("This is a deliberate catalog compilation failure")
}
The operator has reconfigured the site.pp to deliberately fail a catalog compilation when compiling the catalog for the mail server. The web server, however, should still produce a valid catalog. He then verifies these expectations in
Listing 8-47
by re-running the cucumber-puppet command.
Listing 8-47.
Verifying that catalog failures are caught by cucumber-puppet
# cucumber-puppet features/catalog/policy.feature
Feature: General policy for all catalogs
In order to ensure applicability of a host's catalog
As a manifest developer
I want all catalogs to obey some general rules
Scenario Outline: Compile and verify catalog
Given a node specified by "features/yaml/.example.com.yaml"
When I compile its catalog
Then compilation should succeed
And all resource dependencies should resolve
Examples:
| hostname |
| www |
|This is a deliberate catalog compilation failure at /etc/puppet/manifests/site.pp:17
on node mail.example.com
mail |
exit (SystemExit)
features/catalog/policy.feature:8:in `When I compile its catalog'
Failing Scenarios:
cucumber features/catalog/policy.feature:6 # Scenario: Compile and verify catalog
2 scenarios (1 failed, 1 passed)
8 steps (1 failed, 2 skipped, 5 passed)
0m0.132s
The operator uses the fail function to deliberately fail the catalog compilation for the mail server. As expected, cucumber-puppet reports one failed and one successful scenario. The specific error message and line number is returned in the output of cucumber-puppet, allowing the operator to quickly correct the problem.
So far, you've learned how the operator configures cucumber-puppet to test and validate catalog compilation. Next, you'll see how to validate that specific critical resources remain part of the configuration catalog when changes are made to the Puppet modules and manifests.
In the previous section, you learned how to validate catalog compilation successfully after making changes to the Puppet manifests. In this section, you'll see how the operator adds additional scenarios to ensure specific resources remain defined in the configuration catalog.
Catalog compilation errors will be obvious when they occur; the affected nodes will no longer receive a valid catalog. A resource omitted from the catalog is much more difficult to identify, however, because the affected node will still receive and apply the rest of the Puppet catalog. Cucumber-puppet allows the operator to make changes to conditional logic and verify that key resources are not excluded by the change. Let's see how this works.