Managing Infrastructure with Puppet - James Loope [4]
package { 'ntp': ensure => installed }
file { 'ntp.conf':
path => '/etc/ntp.conf',
mode => 640
content => '
driftfile /var/lib/ntp/ntp.drift
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
server 0.pool.ntp.org
server 1.pool.ntp.org
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
restrict 127.0.0.1
restrict ::1
',
require => Package[ntp],
}
A few notes here about the syntax: The capitalization of type in resources is important. You can see that when the resources file and package are declared, they are not capitalized, but when the file resource references the ntp package, it is capitalized. Always capitalize the first letter in the type when you are referring to a resource that you have declared elsewhere, but do not capitalize the type in the declaration itself. Also notice that the package declaration at the top is a sort of shortened form, leaving out line breaks and the comma at the end of the single parameter. The last comma is optional on a parameter list, but it is generally included in the full form.
The path, mode, and content parameters are fairly mundane, but the require parameter is special magic. The Puppet agent doesn’t have any innate sense of order of execution when it is run on a manifest or set of manifests. Things will happen in random sequence unless constrained by some dependencies. require is one of those dependencies. The above statement specifies that the file definition ntp.conf requires that the package ntp be installed before it is created. Conversely, we could have specified in the package declaration for ntp that it be run before => File['ntp.conf']. Next, we’ll look at a slightly more streamlined implementation:
package { 'ntp': ensure => '1:4.2.6.p2+dfsg-1ubuntu5' }
file { '/etc/ntp.conf':
mode => '640',
owner => root,
group => root,
source => '/mnt/nfs/configs/ntp.conf',
require => Package[ntp],
}
The most obvious change here is that we’ve moved the file content to an external source. We’ve told Puppet to go and look in /etc/nfs/configs for a file named ntp.conf and put it in /etc/ntp.conf. For the moment, we’ll use an NFS mount to distribute our configuration files. In later examples, we can use Puppet’s built-in artifice for that purpose. It’s good practice to specify both file permissions and ownership in your manifests, as well as package versions. I’ve replaced the ensure value with an explicit ntp package version. Puppet is intended to be used to make configuration changes as well as to ensure the correctness of configurations. You can think of it both as a deployment script and an auditing tool; by being explicit with your definitions, you can be very confident that your deployment will always work the same way. Finally, I’ll note that this file resource lacks an explicit path parameter. This is because, in Puppet, each type has a parameter that defaults to the resource name. This is referred to as the namevar, and for the file type, it is the source.
Services and Subscriptions
Let’s add a watchdog to ensure that the ntp daemon that we’ve installed is actually running. This will give us some insurance that the proper services have been started, but by no means should it be considered a replacement for a service manager daemon.
I’ve added a service definition that subscribes to the ntp package and its configuration file. On execution, this definition will look in the process table for the pattern “ntpd”. If it fails to find a match for the pattern, Puppet will start the ntp service to ensure that it is running. It also holds a subscription to the ntp package and the file at /etc/ntp.conf. If we later change the config file or