• No results found

Edit the Puppet master site.pp file, and specify which host should run our module

Parameters Class (params.pp)

2. Edit the Puppet master site.pp file, and specify which host should run our module

}

In continuing to borrow from object-oriented programming principles, Puppet uses the inherits keyword to indicate the relationship between our main manifest (init.pp) and our params class (class apache {...} inherits apache::params). An optional change that I recommend is to have our class definition become a parameterized class with default values that correspond to the data in params.pp. I say “optional” because it is sufficient to declare the class without parameters. The inherits statement would allow us to call the names of the variables in the params file without explicitly

specifying the scope. Again, for the sake of facilitating easier code maintenance, it is

highly recommended that you do things such as explicitly defining scope. Other than these changes, the rest of the manifest remains the same.

Before we move on, let’s take this opportunity to try applying the new version of our apache module in prototypical master-agent fashion.

This is a two- or three-step process:

1. Clone your Apache web server module from your remote repository to the correct path on your Puppet master. (Confirm with puppet config print

modulepath.)

2. Edit the Puppet master site.pp file, and specify which host should run our module.

3. (Optional) Tell the Puppet agent node to apply the Puppet code that we have defined. Alternatively, you could just wait for the next automated Puppet agent execution, but where’s the fun in that?

Step 1 should be fairly easy to figure out. Commit the changes we have made so far, and push them to your remote repository. On the Puppet master server, use Git to clone the remote repository to your modulepath. You should then have the tree structure shown in Figure 5-1 under your Puppet Master’s modules directory.

Figure 5-1 The apache module’s directory structure

Step 2 requires editing the site.pp file found in the

/etc/puppetlabs/puppet/environments/production/manifests directory on your Puppet master. You will notice that there is a default entry that we can use as a template for defining our nodes. Let’s make two copies of the default entry and name them after our two puppet nodes: If you’re following my naming convention, the fully qualified domain names (FQDN) will be puppetnode01.devops.local and puppetnode02.devops.local.

Decide which of your Puppet agent servers will be the web server and include the apache module that we’ve created. (In my example, puppetnode01 will be the web server.) Your site.pp settings should look similar to Listing 5-4.

Listing 5-4 site.pp Entries for Your Puppet Agent Servers

Click here to view code image node default {

# This is where you can declare classes for all nodes.

# Example:

# class { ‘my_class’: } }

node ‘puppetnode01.devops.local’ { class { ‘apache’: }

}

node ‘puppetnode02.devops.local’ { # We’ll fill this in later }

Step 3: Let’s run the following command on our Puppet agent server:

puppet agent –td

Wait a minute. Why is the command puppet agent and not puppet apply? The agent tells the agent to execute the instructions that the Puppet master has for it. The apply command is for ad hoc executions of local puppet manifests.

The first time that you run the puppet agent command, you will get a certificate error, and no instructions will be executed on the agent. This is because the Puppet master has to authorize the Puppet agent servers. On the Puppet master server, you can see the list of Puppet agent servers with a certificate signing request (CSR) by using the command puppet cert list. In production, you want to be selective of which requests you authorize. However, because this is just a test environment, you will go ahead and sign all the requests using the command puppet cert sign --all.

After the Puppet master confirms that the requests were processed, go ahead and rerun the puppet agent -td command on the Puppet agent server. Try accessing your new web server from a browser to verify that Apache was installed and configured properly.

Note

Puppet enables you to preview the changes that your puppet agent and puppet apply executions will make by using the --noop option with these commands (that is, puppet agent --noop -td). The noop command tells Puppet that you want to see only the changes that it will make, instead of actually implementing them.

Hiera

Params.pp is great. However, what happens when we want to consider additional operating systems besides just CentOS and Ubuntu? We must update the conditional statement in params.pp. Even though params.pp is fairly short, consider a larger list of values with multiple conditional statements based on various system facts. Wouldn’t it be great if we could let Puppet determine which data to apply at the appropriate time instead of having to write more conditional statements ourselves? Enter Hiera.

Hiera is an open source utility that ships with Puppet 3.x versions. (To use Hiera with older Puppet versions, a separate manual installed is required.) Hiera is a popular alternative for separating code from data. Hiera allows you to store key:value data in a variety of data stores including YAML (YAML Ain’t Markup Language) files, JSON (JavaScript Object Notation) files, and MySQL databases. For the Hiera examples in this book, we use YAML.

When we want to reference the values that are stored in Hiera, we would use the

command, hiera(‘value’). If we implement Hiera in our apache module, our init.pp manifest could look like Listing 5-5.

Listing 5-5 Apache Module init.pp with Hiera

Click here to view code image class apache {

$webserver= hiera(‘webserver’) $confpath = hiera(‘confpath’) $htmlpath = hiera(‘htmlpath’) package { ‘apache’:

name => $webserver, ensure => installed, }

file {‘apacheconf’:

name => $confpath, ensure => file, mode => 600,

source => “puppet:///modules/apache/$webserver.conf”, require => Package[‘apache’],

}

service {‘apache’:

name => $webserver, ensure => running, enable => true,

subscribe => File[‘apacheconf’], }

file {‘apachecontent’:

name => $htmlpath, ensure => file, mode => 644,

content => template(‘apache/index.html.erb’), require => Service[‘apache’],

} }

Let’s take a look at how to get our system ready to use Hiera. First, we need to configure Hiera to work properly with our data.

The Hiera configuration file is found at /etc/puppetlabs/puppet/hiera.yaml in Puppet Enterprise (/etc/puppet/hiera.yaml with open source Puppet). Hiera’s settings are defined in the YAML format, as you may have guessed from the configuration file’s extension.

Listing 5-6 displays a sample Hiera configuration file. Make a backup copy of your hiera.yaml file (ex: hiera.yaml.old). Then, change the contents of your hiera.yaml file to match the settings in Listing 5-6. Be careful with spacing.

Listing 5-6 hiera.yaml

Click here to view code image

:backends:

- yaml :hierarchy:

- defaults

- “%{::osfamily}”

- “%{::fqdn}”

- global :yaml:

# datadir is empty here, so hiera uses its defaults:

# - /var/lib/hiera on *nix

# - %CommonAppData%\PuppetLabs\hiera\var on Windows

# When specifying a datadir, make sure the directory exists.

:datadir: ‘/etc/puppetlabs/puppet/hieradata’

The :backends: section tells Hiera how the key:value pairs will be stored. Again, we are using YAML in our lab.

The :hierarchy: section tells Hiera in which files to find your key:value pair data.

This section can use system information from facter (introduced in the previous chapter) in case you wanted to make YAML files according to the operating system family, for example. So, if we want to have specific settings for Debian-based operating systems, like Ubuntu, we would place them in a file called Debian.yaml. (You’ll see an example of this little bit later.) Hiera will search for values based on the order that is listed in this section of the configuration file.

Based on the sample hiera.yaml file in Listing 5-6, if you ask Hiera to look up a variable named $webserver, it will first look for it in a file called defaults.yaml. If defaults.yaml does not have a value for $webserver, or if the file doesn’t exist, it will look for a

YAML file whose name matches the osfamily value for the agent server currently

being evaluated (for example, Debian.yaml). The process will continue on until Hiera finds the value it is looking for or until it exhausts searching in global.yaml. If the value doesn’t exist in global.yaml, Hiera will return a nil value to the manifest compilation process that called it.

The :yaml: section tells Hiera where we will be storing all of the YAML data files that we specified in the :hierarchy: section. For our apache module, I would create two data files to match the value returned by the "%{osfamily}" fact (RedHat for Red Hat, Fedora, and CentOS systems; and Debian for Debian and Ubuntu systems): Debian.yaml and RedHat.yaml. Remember to place these files in the :datadir: path that you

specified (/etc/puppetlabs/puppet/hieradata in my example) (see Listing 5-7).

Listing 5-7 Operating System-Specific YAML Files

Click here to view code image Debian.yaml

webserver:

- ‘apache2’

confpath:

- ‘/etc/apache2/apache2.conf’

htmlpath:

- ‘/var/www/index.html’

RedHat.yaml

webserver:

- ‘httpd’

confpath:

- ‘/etc/httpd/conf/httpd.conf’

htmlpath:

- ‘/var/www/html/index.html’

Data separation solely for the purpose of writing clean code may seem trivial, but consider another use case: management of sensitive data such as passwords. If you will be storing your manifests in a repository for others to access, you might not necessarily want the entire team to have access to passwords for critical resources. At some point, that manifest may be viewed by folks who do not have sufficient authorization to have root access to critical resources like database or DNS servers. Even when you have secure access control for the Hiera files, you may want to encrypt any sensitive data using a plugin like hiera-gpg to keep important data safe.