Pro Puppet (17 page)

Read Pro Puppet Online

Authors: Jeffrey McCune James Turnbull

BOOK: Pro Puppet
2.96Mb size Format: txt, pdf, ePub
class apache::service {
  service { "apache2":
    ensure => running,
    hasstatus => true,
    hasrestart => true,
    enable => true,
    require => Class["apache::install"],
  }
}

This has allowed us to manage Apache, but how are we going to configure individual websites? To do this we’re going to use a new syntax, the definition.

The Apache definition

Definitions are also collections of resources like classes, but unlike classes they can be specified and are evaluated multiple times on a host. They also accept parameters.

Note
Remember that classes are singletons. They can be included multiple times on a node, but they will only be evaluated ONCE. A definition, because it takes parameters, can be declared multiple times and each new declaration will be evaluated.

We create a definition using the
define
syntax, as shown in
Listing 2-7
.

Listing 2-7.
The First Definition

define apache::vhost( $port, $docroot, $ssl=true, $template='apache/vhost.conf.erb', $priority, $serveraliases = '' ) {
  include apache
  file {"/etc/apache2/sites-enabled/${priority}-${name}":
    content => template($template),
    owner => 'root',
    group => 'root',
    mode => '777',
    require => Class["apache::install"],
    notify => Class["apache::service"],
  }
}

We gave a definition a title (
apache::vhost
) and then specified a list of potential variables. Variables can be specified as a list, and any default values specified, for example
$ssl=true
. Defaults will be overridden if the parameter is specified when the definition is used.

Inside the definition we can specify additional resources or classes, for example here we’ve included the
apache
class that ensures all required Apache configuration will be performed prior to our definition being evaluated. This is because it doesn’t make sense to create an Apache VirtualHost if we don’t have Apache installed and ready to serve content.

In addition to the
apache
class, we’ve added a basic file resource which manages Apache site files contained in the
/etc/apache2/sites-enabled
directory. The title of each file is constructed using the
priority
parameter, and the title of our definition is specified using the
$name
variable.

Tip
The
$name
variable contains the name, also known as the title, of a declared defined resource. This is the value of the string before the colon when declaring the defined resource.

This file resource’s
content
attribute is specified by a template, the specific template being the value of the
$template
parameter. Let’s look at a fairly simple ERB template for an Apache VirtualHost in
Listing 2-8
.

Listing 2-8.
VirtualHost Template

NameVirtualHost *:<%= port %>
>
  ServerName <%= name %>
<%if serveraliases.is_a? Array -%>
<% serveraliases.each do |name| -%><%= "  ServerAlias #{name}\n" %><% end -%>
<% elsif serveraliases != '' -%>
<%= "  ServerAlias #{serveraliases}" -%>
<% end -%>
  DocumentRoot <%= docroot %>
  >
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
  

  ErrorLog /var/log/apache2/<%= name %>_error.log
  LogLevel warn
  CustomLog /var/log/apache2/<%= name %>_access.log combined
  ServerSignature On

Each parameter specified in the definition is used, including the
$name
variable to name the virtual host we’re creating.

You can also see some embedded Ruby in our ERB template:

<%if serveraliases.is_a? Array -%>
<% serveraliases.each do |name| -%><%= "  ServerAlias #{name}\n" %><% end -%>
<% elsif serveraliases != '' -%>
<%= "  ServerAlias #{serveraliases}" -%>
<% end -%>

Here we’ve added some logic to the
serveraliases
parameter. If that parameter is an array of values, then create each value as a new server alias; if it’s a single value, then create only one alias.

Let’s now see how we would use this definition and combine our definition and template:

apache::vhost { 'www.example.com':
  port => 80,
  docroot => '/var/www/www.example.com',
  ssl => false,
  priority => 10,
  serveraliases => 'home.example.com',
}

Here we have used our definition much the same way we would specify a resource by declaring the apache::vhost definition and passing it a name,
www.example.com
(which is also the value of the
$name
variable). We’ve also specified values for the required parameters. Unless a default is already specified for a parameter, you need to specify a value for every parameter of a definition otherwise Puppet will return an error. We could also override parameters, for example by specifying a different template:

template => 'apache/another_vhost_template.erb',

So in our current example, the template would result in a VirtualHost definition that looks like
Listing 2-9
.

Listing 2-9.
The VirtualHost Configuration File

NameVirtualHost *:80

  ServerName www.example.com
  ServerAlias home.example.com
  DocumentRoot /var/www/www.example.com
  
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
  

  ErrorLog /var/log/apache2/www.example.com_error.log
  LogLevel warn
  CustomLog /var/log/apache2/www.example.com_access.log combined
  ServerSignature On

The final class in our module is the
apache
class in the
init.pp
file, which includes our Apache classes:

class apache {
  include apache::install, apache::service
}

You can see we’ve included our three classes but not the definition,
apache::vhost
. This is because of some module magic called “autoloading.” You learned how everything in modules is automatically imported into Puppet, so you don’t need to use the import directive. Puppet scans your module and loads any .pp file in the manifests directory that is named after the class it contains, for example the
install.pp
file contains the
apache::install
class and so is autoloaded.

The same thing happens with definitions: The
vhost.pp
file contains the definition
apache::vhost,
and Puppet autoloads it. However, as we declare definitions, for example calling
apache::vhost
where we need it, we don’t need to do an
include apache::vhost
because calling it implies inclusion.

Next, we include our classes into our
www.example.com
node and call the
apache::vhost
definition to create the
www.example.com
website.

node "www.example.com" {
  include base
  include apache
  apache::vhost { 'www.example.com':
    port => 80,
    docroot => '/var/www/www.example.com',
    ssl => false,
    priority => 10,
    serveraliases => 'home.example.com',
  }    
}

We could now add additional web servers easily and create additional Apache VirtualHosts by calling the
apache::vhost
definition again, for example:

apache::vhost { 'another.example.com':
    port => 80,
    docroot => '/var/www/another.example.com',
    ssl => false,
    priority => 10,
}
Managing Puppet with the Puppet Module

In our very last module we’re going to show you Puppet being self-referential, so you can manage Puppet with Puppet itself. To do this we create another module, one called
puppet
, with a structure as follows:

puppet
puppet/files/
puppet/manifests/init.pp
puppet/manifests/install.pp
puppet/manifests/config.pp
puppet/manifests/params.pp
puppet/manifests/service.pp
puppet/templates/puppet.conf.erb

Our first class will be the
puppet::install
class which installs the Puppet client package.

class puppet::install {
  package { "puppet" :
    ensure => present,
  }
}

All of the operating systems we’re installing on call the Puppet package
puppet,
so we’re not going to use a variable here.

We do, however, need a couple of variables for our Puppet module, so we add a
puppet::params
class.

class puppet::params {
  $puppetserver = "puppet.example.com"
}

For the moment, this class only contains a Puppet server variable that specifies the fully-qualified domain name (FQDN) of our Puppet master.

Now we create our
puppet::config
class:

class puppet::config {
include puppet::params
  file { "/etc/puppet/puppet.conf":
    ensure = > present,
    content => template("puppet/puppet.conf.erb"),
    owner => "puppet",
    group => "puppet",
    require => Class["puppet::install"],
    notify => Class["puppet::service"],
  }
}

This class contains a single file resource that loads the
puppet.conf.erb
template. It also includes the
puppet::params
class so as to make available the variables defined in that class. Let’s take a look at the contents of our template too:

[main]
    user = puppet
    group = puppet
    report = true
    reports = log,store
[master]
    certname = <%= puppetserver %>
[agent]
    pluginsync = false
    report = true
    server = <%= puppetserver %>

This is a very simple template, which we can then expand upon, or you can easily modify to add additional options or customize for your own purposes. You’ll notice we’ve included configuration for both our master and the client. We’re going to manage one
puppet.conf
file rather than a separate one for master and client. This is mostly because it’s easy and because it doesn’t add much overhead to our template.

We can then add the
puppet::service
class to manage the Puppet client daemon.

class puppet::service {
  service { "puppet":
    ensure => running,
    hasstatus => true,
    hasrestart => true,
    enable => true,
    require => Class["puppet::install"],
  }
}

We can then create an
init.pp
that includes the
puppet
class and the sub-classes we’ve just created:

class puppet {
  include puppet::install, puppet::config, puppet::service
}

Just stopping here would create a module that manages Puppet on all our clients. All we need to do, then, is to include this module on all of our client nodes, and Puppet will be able to manage itself. But we’re also going to extend our module to manage the Puppet master as well. To do this, we’re going to deviate slightly from our current design and put all the resources required to manage the Puppet master into a single class, called
puppet::master
:

class puppet::master {
include puppet
include puppet::params
package { "puppet-server":
  ensure => installed,
}
service { "puppetmasterd":
    ensure => running,
    hasstatus => true,
    hasrestart => true,
    enable => true,
    require => File["/etc/puppet/puppet.conf"],
  }
}

You can see that our class
puppet::master
includes the classes
puppet
and
puppet::params
. This will mean all the preceding Puppet configuration will be applied, in addition to the new package and service resources we’ve defined in this class.

Other books

Stone Kiss by Faye Kellerman
by Unknown
The Halifax Connection by Marie Jakober
Hell Fire by Karin Fossum
Sex, Lies and Surveillance by Stephanie Julian
A Promise Is for Keeping by Felicity Hayle