Managing Infrastructure with Puppet - James Loope [9]
We can build a base class that installs our Apache2 package and sets up a service to manage it. We can then inherit from the base and add a couple of special-purpose classes for an Apache with SSL and Apache with PHP. This helps to reduce duplication in our code and makes future changes less onerous:
class http-server {
package { "apache2": ensure => installed }
service { "apache2":
ensure => running,
enable => true,
pattern => "apache2",
subscribe => Package["apache2"],
}
}
class https-server inherits http-server {
exec { "a2enmod ssl":
creates => "/etc/apache2/mods-enabled/ssl.load",
notify => Service["apache2"],
}
}
class http-php-server inherits http-server {
package { "libapache2-mod-php5": ensure => installed }
exec { "a2enmod php5":
creates => "/etc/apache2/mods-enabled/php5.load",
notify => Service["apache2"],
}
file { "/etc/php5/apache2/php.ini":
source => "puppet:///modules/apps/php5/php.ini",
notify => Service["apache2"],
}
}
We can also pass parameters into classes when we include them in a node. Say we need to install different package versions on different systems. We can build a class that accepts a parameter $version and then passes that on to a package resource inside.
Here I’ve built a class called ruby that accepts a parameter named version that has a default value of ‘1.8’. Then I’ve declared the ruby class in my example node, passing the version ‘1.9.1’. If I omit the version parameter, the package will be installed with the default value, but I am otherwise allowed to override the version with whatever I choose:
class ruby ( $version = '1.8') {
$package_list = ["ruby$version",
"ruby$version-dev",
"rubygems$version",
"libopenssl-ruby$version",]
package { $package_list:
ensure =>installed,
}
}
node "test.example.com" {
class { 'apps::ruby':
version => "1.9.1",
}
}
* * *
Note
In the last example, I built an array of package names and passed them into a package resource. This is an example of a custom variable declaration.
* * *
Virtual Resources
Resources in Puppet can only be declared once per client configuration. This can make managing resources that relate to multiple configurations complicated. To alleviate this issue, we have the virtual resource. When marked with a @ prefix, a resource can be declared, but not yet applied to the client. When we want to apply it to a particular client, it must be realized first.
I’ve declared three virtual users: bob, alice, and eve, each of which has a different group membership. If I’d like to realize one of these users, I could use the realize User[username] mechanism. It could be tedious to realize each of your users in this fashion. In order to realize virtual resources in groups, specified by their attributes, we can use collections. The last two statements are collections; each is a collection of users defined by their membership in a particular group. The first collection will contain alice and eve and the second will contain all three:
@user { bob:
ensure => present,
groups => [ "mail", "web" ],
}
@user { alice:
ensure => present,
groups => [ "db", "web" ],
}
@user { eve:
ensure => present,
groups => [ "db", "web", "mail", "admin" ],
}
User <| group == db |>
User <| group == web |>
Variables
Variables are denoted by a $ prefix and can be declared as arrays or hashes. They can be scoped locally to a class or globally if declared outside a class. Class-scoped variables are also available publicly by qualifying their parent class:
class classone {
$variableone = 'test'
$variabletwo = [ 'foo', 'bar', 'baz', 'qux' ]
$variablethree = { foo => 'bar', baz => 'qux' }
}
class classtwo {
$variableone = $classone::variableone
$variabletwo = $classone::variabletwo[1]
$variablethree = $classtwo::variablethree[foo]
}
This can be useful in some circumstances, but it is somewhat difficult to ensure predictable behavior, as the values are dependent on the order of evaluation of the two classes. If you need to use a variable from