Authors: Jeffrey McCune James Turnbull
First, the operator adds another step to the catalog policy. This step expresses his requirement that every Puppet catalog manages alocaladmin
account. This ensures that he'll always be able to log in to the systems Puppet manages. The local administrator account will grant access even if the central LDAP server is down. With theaccounts_ruby
module introduced in this chapter, accounts may no longer be directly managed in the Puppet manifests. Instead, external data supplied through the ENC script determines the accounts that are and are not managed by Puppet. This configuration introduces the possibility of the external data changing and therefore thelocaladmin
account being omitted from the configuration catalog.
Adding a step to the cucumber-puppet policy is straightforward and readable. Let's see what the change and resulting policy check look like in
Listing 8-48
.
Listing 8-48.
Adding a step to the catalog policy
# git diff /etc/puppet/features/catalog/policy.feature
diff --git a/features/catalog/policy.feature b/features/catalog/policy.feature
index ea81ae0..51f374e 100644
--- a/features/catalog/policy.feature
+++ b/features/catalog/policy.feature
@@ -8,6 +8,7 @@ Feature: General policy for all catalogs
When I compile its catalog
Then compilation should succeed
And all resource dependencies should resolve
+ And it should have a localadmin account
Examples:
| hostname |
The operator adds a single line to the policy file describing the step cucumber-puppet should validate. He hasn't yet implemented this check or step, but cucumber-puppet provides useful information to guide the process. In
Listing 8-49
the operator runs cucumber-puppet to see what happens when an unimplemented step is encountered.
Listing 8-49.
Cucumber-puppet with missing steps
# 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
And it should have a localadmin account
Examples:
| hostname |
| login |
Undefined step: "it should have a localadmin account" (Cucumber::Undefined)
features/catalog/policy.feature:11:in `And it should have a localadmin account'
1 scenario (1 undefined)
5 steps (1 undefined, 4 passed)
0m0.421s
You can implement step definitions for undefined steps with these snippets:
Then /^it should have a localadmin account$/ do
pending # express the regexp above with the code you wish you had
end
Cucumber-puppet provides helpful output about how to add the step definition using a template snippet. Let's see how the operator uses this information to validate the administrator account. First, he copies and pastes the snippet into the file /etc/puppet/features/steps/user.rb. Then, he runs cucumber puppet in
Listing 8-50
to verify that the step is being matched by the regular expression.
Listing 8-50.
Adding a pending step to 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 #
features/catalog/policy.feature:6
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
And it should have a localadmin account
Examples:
| hostname |
| login |
TODO (Cucumber::Pending)
./features/steps/user.rb:18:in `/^it should have a localadmin account$/'
features/catalog/policy.feature:11:in `And it should have a localadmin account'
1 scenario (1 pending)
5 steps (1 pending, 4 passed)
0m0.516s
After adding the step snippet to the user.rb file, running cucumber-puppet indicates the step is pending. This validates that the regular expression is matching the line added to the policy. Next, the operator modifies the regular expression for the step. In addition, he adds the bit of Ruby code shown in
Listing 8-51
to ensure that the account resource is declared.
Listing 8-51.
Implementing validation of the localadmin user resource
# git diff
diff --git a/features/steps/user.rb b/features/steps/user.rb
index 92e0170..1644d28 100644
--- a/features/steps/user.rb
+++ b/features/steps/user.rb
@@ -14,6 +14,8 @@ Then /^the user should be in groups "([^\"]*)"$/ do |groups|
fail unless g_s == groups
end
-Then /^it should have a localadmin account$/ do
- pending # express the regexp above with the code you wish you had
+Then /^it should have a (\w+) account$/ do |user|
+ steps %Q{
+ Then there should be a resource "User[#{user}]"
+ }
End
The operator adjusts the regular expression to match any user account, not just the specificlocaladmin
account. The \w+ (word character) portion of the regular expression performs this match. In addition, the surrounding parentheses cause the word within to be placed in theuser
variable. Finally, an additional step is added, substituting the name of the user account inside the resource title.
Running cucumber-puppet in
Listing 8-52
indicates that all steps are passing successfully.
Listing 8-52.
Cucumber-puppet validating the localadmin account
# 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
And it should have a localadmin account
Examples:
| hostname |
| login |
1 scenario (1 passed)
5 steps (5 passed)
0m0.391s
The operator isn't convinced yet. He wants to make sure that cucumber-puppet will catch the situation where the administrator account is not set by the ENC. To test this situation, he modifies the ENC script shown in
Listing 8-32
and removes the administrator account. This updated script is shown in
Listing 8-53
.
Listing 8-53.
Removing the administrator account from the ENC
# git diff /etc/puppet/resoruces_enc.rb
diff --git a/resources_enc.rb b/resources_enc.rb
index bd0e46e..d7a10c5 100755
--- a/resources_enc.rb
+++ b/resources_enc.rb
@@ -25,15 +25,15 @@ require 'yaml'
# parameter. These values could come from LDAP, SQL, etc...
@out["parameters"]['account_resources'] = Hash.new
-@out["parameters"]['account_resources']["localadmin"] = {
- "comment" => "Local Administrator",
- "home" => "/home/localadmin",
- "uid" => 600,
- "gid" => 600,
- "groups" => [ "sudo", "sudo_nopw", "ops" ],
- "shell" => "/bin/bash",
- "password" => "!!",
-}
+# @out["parameters"]['account_resources']["localadmin"] = {
+# "comment" => "Local Administrator",
+# "home" => "/home/localadmin",
+# "uid" => 600,
+# "gid" => 600,
+# "groups" => [ "sudo", "sudo_nopw", "ops" ],
+# "shell" => "/bin/bash",
+# "password" => "!!",
+# }
@out["parameters"]['account_resources']["alice"] = {
"comment" => "Alice",
The operator simply commented out the administrator account from the external node classifier script. The cucumber-puppet node cache must be updated for the new test to be valid. To update the cache, the operator runs Puppet Agent as shown in
Listing 8-54
and then copies the YAML node file into the features directory.
Listing 8-54.
Updating the cucumber-puppet node cache
# cp /var/lib/puppet/yaml/node/login.example.com.yaml \
/etc/puppet/features/yaml/
After updating the node definition, the operator validates that cucumber-puppet catches the missing administrator account (see
Listing 8-55
).
Listing 8-55.
Cucumber-puppet identifies the missing administrator account
# 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
And it should have a localadmin account
Examples:
| hostname |
| login |
(RuntimeError)
./features/steps/puppet.rb:69:in `/^there should be a resource "([^\"]*)"$/'
features/catalog/policy.feature:11:in `And it should have a localadmin account'
Failing Scenarios:
cucumber features/catalog/policy.feature:6 # Scenario: Compile and verify catalog
1 scenario (1 failed)
5 steps (1 failed, 4 passed)
0m0.369s
As expected, cucumber-puppet catches the missing user resource. The operator is confident now that the additional step is properly catching the missing account resource.
As you've just seen, cucumber-puppet provides a convenient and easy to use method for testing Puppet Catalogs. By defining policy steps for resources that should by managed, the operator remains confident while making changes. If the Local Administrator account information is no longer set by the ENC script, the problem will be quickly caught. In addition, changes to the puppet manifests and modules can be made with confidence so long as critical resources are being tested and verified with cucumber-puppet.
In this chapter, you've seen a number of tools related to, and part of, Puppet 2.6. First, the Puppet Module tool provides a command line interface to working with Puppet modules. The operator is able to use puppet-module to generate module skeleton templates. Once the configuration code is filled into the template, the operator is able to package and publish the module toforge.puppetlabs.com
. Even if you don't plan to publish modules to the Forge, puppet-module provides the means to track module versions and install them into your own Puppet configuration. In addition to generating skeleton templates for modules, puppet-module allows you to search, download and install publically-available modules. You learned how the operator easily installed and made use of a module managing the host-based iptables firewall. Downloading and using public modules greatly reduces time and effort.
In addition to the module tool, you learned about the new Ruby DSL in Puppet 2.6. Using the Ruby language allowed the developer to declare an arbitrary number of account resources in Puppet. Without the ability to iterate in Ruby, the developer would have had a difficult time managing a growing number of accounts with Puppet. Puppet version 2.6 allows both Ruby and Puppet manifests to be intermixed in the same catalog run, and even within the same module.
Finally, you learned about a unique and novel approach to testing Puppet catalogs with the cucumber-puppet tool. Inspired by Cucumber, the framework encourages a natural language specification of requirements. In addition, cucumber-puppet provides a very helpful and intuitive interface. Once a specification is written, helpful boilerplate code is given back if cucumber-puppet does not yet understand how to validate the specification. Using this boilerplate allows tests to be quickly implemented and written without a deep understanding of Ruby.