Pro Puppet (49 page)

Read Pro Puppet Online

Authors: Jeffrey McCune James Turnbull

BOOK: Pro Puppet
13.67Mb size Format: txt, pdf, ePub

First, the operator adds another step to the catalog policy. This step expresses his requirement that every Puppet catalog manages a
localadmin
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 the
accounts_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 the
localadmin
account being omitted from the configuration catalog.

Adding a Check Step to the Policy

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
Implementing the Cucumber Check

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 specific
localadmin
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 the
user
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
Catching Changes in the ENC Data

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.

Summary

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 to
forge.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.

Other books

Good Day In Hell by J.D. Rhoades
What You Make It by Michael Marshall Smith
DEBT by Jessica Gadziala
The Hopefuls by JENNIFER CLOSE
Villain a Novel (2010) by Yoshida, Shuichi
Full Ride by Margaret Peterson Haddix
Serial Killer vs. E-Merica by Robert T. Jeschonek
The Book of Revelation by Rupert Thomson