Authors: Jeffrey McCune James Turnbull
As we can see, Puppet is being run in no-operation mode, and would have created six resources: three for Bob and three for Alice, each person having a user, group and home directory managed for them. The developer has not explicitly declared any relationships among these resources, Puppet is managing them using the implicit relationships between file owners and groups and a user resources relationship to its group members. The relationship graph looks like that shown in
Figure 8-1
.
Figure 8-1.
Relationship graph for accounts_ruby
Adding new accounts managed by Puppet is now simply a matter of setting them in the ENC. No code in Puppet needs to be changed. Let's see how the developer adds a new account in
Listing 8-30
. The ENC script is modified to produce a third entry in theaccount_resources
parameter for a local administrator account.
Listing 8-30.
Extending the Ruby DSL ENC to have a third account resource
# /etc/puppet/resources_enc.rb
---
parameters:
enc_location: Florida
account_resources:
alice:
groups:
- sudo
- sudo_nopw
- devel
comment: Alice
gid: 601
uid: 601
shell: /bin/bash
password: "!!"
home: /home/alice
localadmin:
groups:
- sudo
- sudo_nopw
- ops
comment: Local Administrator
gid: 600
uid: 600
shell: /bin/bash
password: "!!"
home: /home/localadmin
bob:
groups:
- sudo
- sudo_nopw
- ops
comment: Bob
gid: 602
uid: 602
shell: /bin/zsh
password: "!!"
home: /home/bob
classes:
- motd_location
- accounts_ruby
Notice there is now a third entry in the output of the external node classifier script. This information contains only the data related to the Local Administrator account. The modification to the script is simply a matter of adding the hash entry to theaccount_resources
object, as you can see in the diff shown in
Listing 8-31
.
Listing 8-31.
Adding a third account resource to the ENC script
# git diff
diff --git a/resources_enc.rb b/resources_enc.rb
index d7a94d9..bd0e46e 100755
--- a/resources_enc.rb
+++ b/resources_enc.rb
@@ -25,6 +25,16 @@ 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']["alice"] = {
"comment" => "Alice",
"home" => "/home/alice",
The developer is directly declaring Hash resources in the script, but this information could just as easily come from YAML data files stored on disk, LDAP or an SQL database. Finally, the developer runs
Puppet as shown in
Listing 8-32
. Puppet manages additional resources based on the new data coming through the ENC API.
Listing 8-32.
Puppet adding additional resources from the ENC information
$ puppet apply --verbose --noop /etc/puppet/manifests/site.pp
notice: /Stage[main]/Accounts_ruby/Group[alice]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/Group[localadmin]/ensure: is absent, should be present
(noop)
notice: /Stage[main]/Accounts_ruby/User[alice]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/File[/home/alice]/ensure: is absent, should be directory
(noop)
notice: /Stage[main]/Accounts_ruby/User[localadmin]/ensure: is absent, should be present
(noop)
notice: /Stage[main]/Accounts_ruby/File[/home/localadmin]/ensure: is absent, should be
directory (noop)
notice: /Stage[main]/Accounts_ruby/Group[bob]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/User[bob]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/File[/home/bob]/ensure: is absent, should be directory (noop)
This shows that Puppet is now managing nine resources instead of the six in the previous run. Puppet is managing the Local Administrator account simply by adding additional data to the external node classifier. This implementation would have been very difficult to carry out using only the Puppet language, since there is no easy way to iterate over a Hash. By using the each method in Ruby, the developer is able to declare an arbitrary and dynamically changing number of resources based on external data.
In the next section, we switch gears and examine another valuable tool in the Puppet ecosystem. Cucumber Puppet allows you to easily and clearly test your Puppet infrastructure.
A common problem with Puppet and configuration management in general is testing. After working with Puppet for some time, the Example.com operator has the nagging question, “How can I test changes I make to my systems with Puppet?” A frequently-used solution to this problem is to maintain an entirely separate network identically configured to the production network. Changes to Puppet are then deployed to the testing network prior to deploying them to production. While effective, this strategy incurs the overhead of maintaining a separate network. Additional hardware and time must be invested in the testing network.
Automated testing tools like Cucumber Puppet do not fully replace a rigorous testing network identically configured to production. However, in situations where a testing network is not available or feasible, Cucumber Puppet solves many of the problems related to testing and change control.
Cucumber Puppet is a tool that allows you to specify the desired behavior of a Puppet configuration catalog. Once specified, the tool also allows you to verify changes to the Puppet modules and manifests, resulting in a configuration catalog with the same specified behavior. This functionality allows you to make changes with confidence, knowing unintended side effects will not be introduced.
If you're already familiar with Cucumber, the specifications used by Cucumber Puppet will be familiar. Cucumber Puppet is inspired by the natural language descriptions of application behavior used in Cucumber. More information about Cucumber is available athttp://cukes.info/
. Let's see how the
Example.com operator installs and uses Cucumber Puppet to test and validate changes to his Puppet configuration.
Similar to the puppet-module tool discussed in this chapter, The Cucumber Puppet tool, cucumber-puppet, is not available as a native package on most operating systems. However, the software is easily installed using the RubyGems gem command (see
Listing 8-33
).
Listing 8-33.
Installing cucumber-puppet with RubyGems
# gem install cucumber-puppet
Building native extensions. This could take a while...
(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
Thank you for installing cucumber-0.10.0.
Please be sure to read http://wiki.github.com/aslakhellesoy/cucumber/upgrading
for important information about this release. Happy cuking!
(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
Successfully installed json-1.4.6
Successfully installed gherkin-2.3.3
Successfully installed term-ansicolor-1.0.5
Successfully installed builder-3.0.0
Successfully installed diff-lcs-1.1.2
Successfully installed cucumber-0.10.0
Successfully installed gem-man-0.2.0
Successfully installed highline-1.6.1
Successfully installed extlib-0.9.15
Successfully installed templater-1.0.0
Successfully installed cucumber-puppet-0.1.1
11 gems installed
If you're installing on a Debian-based system, the gem command may be configured to install executable scripts in a location not in the PATH environment variable. On a Debian system, this location is /var/lib/gems/1.8/bin. If you encounter the “command not found” message shown in
Listing 8-34
, which the operator experiences on a Debian 6.0 system, you may use the gem environment command shown in
Listing 8-35
to find where the executable scripts are installed.
Listing 8-34.
Testing if the cucumber-puppet executable is in the PATH variable
# which cucumber-puppet
cucumber-puppet not found
Listing 8-35.
Using the gem environment to locate the executable directory
# gem environment
RubyGems Environment:
- RUBYGEMS VERSION: 1.3.7
- RUBY VERSION: 1.8.7 (2010-08-16 patchlevel 302) [i486-linux]
- INSTALLATION DIRECTORY: /var/lib/gems/1.8
- RUBY EXECUTABLE: /usr/bin/ruby1.8
- EXECUTABLE DIRECTORY: /var/lib/gems/1.8/bin
- RUBYGEMS PLATFORMS:
- ruby
- x86-linux
- GEM PATHS:
- /var/lib/gems/1.8
- /root/.gem/ruby/1.8
- GEM CONFIGURATION:
- :update_sources => true
- :verbose => true
- :benchmark => false
- :backtrace => false
- :bulk_threshold => 1000
- REMOTE SOURCES:
- http://rubygems.org/