Puppet Course - Day 2 Notes

Notify - puppet debugging

The notify statement can be used in module definitions to print & log useful debugging information.

For instance, if large trees of logic are being traversed, insertion of a notify statement will allow you to determine whether the class is being declared or not.

notify { 'Puppet is now activating Class "blah"': }

or
notify { 'another':
{ 'This is another message'
}

Metaparameters

Parameters which work with any resource tupe

  • Schedule
  • This will prevent puppet from being run outside of the defined window. Similar to operation windows in CommVault.
  • alias
  • Create an alias name for a resource
  • noop
  • Exclude just a single resource from being enforced (whilst the rest of the resources will be enforced)
  • loglevel
  • Set log level to standard syslog levels
  • tag
  • Custom tags for resources - useful for reporting
File Resources

ensure => file, directory, or symlink,
mode => 755,
owner => 'robbie',
group => 'admins',

Some others include

  • Content
  • a string which will become the contents of the file
  • Source
  • will retrieve a file from a remote source (if the local doesn't exist or if the contents are different)
Dependencies
service {'nginx':
require => Package['nginx'],
ensure => 'running',
enable => 'true',
}

would specify that the nginx package needs to be installed before the service can be started.

Note - all resources need to be explicitly defined in Puppet. If for instance a user "root" was required, this would not be valid unless a "root" user resource was also declared (regardless of the fact that the root user exists in /etc/passwd).

Auto restart service
service {'nginx':
subscribe => File['file'],
require => Package['nginx'],
ensure => 'running',
enable => 'true',
}

Will restart the service if there is a change in the speficied file (e.g. the config file). i.e. if Puppet detects that your local copy is not correct and it needs to change that file, it will then restart the service.

subscribe => ['/dir/files1', '/dir1/file2', 'dir2/file1'],

Would watch all three of these files and restart the service if any of these are changed by puppet.

Strings & Variables
  • Variables are immutable
  • Variables must be unique within the scope
  • Variables declared within class apply within the scope
  • Node scope applies to node
  • Top scope is global
  • Single quotes pass literal strings to the var (i.e. ${http_dir wouldn't be evaluated)

$var1 = "my value is ${http_dir} on this system"

Defaults

Default resource attributes can be configured using a capital letter at the beginning, with no title. e.g.

File {
  owner => 'root',
}

Would mean that any file resources defined later would automatically adopt this owner, unless the later resource defines a different attribute value.

file('test.sh':
  name => '/tmp/test.sh',
}

would adopt the default above, whereas the below

file('test.sh':
  name => '/tmp/test.sh',
  owner => 'ted'
}

would override the default and set the owner to "ted"

Case Statements

If you're using lots of logic (perhaps more than two options), case might be a better approach

case $::operatingsystem {
  'redhat', 'centos': { include redhat } # This applies the redhat class
  'debian', 'ubuntu': { include debian } # This applies the debian class
  'windows': { include windows } # This applies the windows class
  'amazon': {
    include amazon             # This applies the amazon class
    include redhat             # This also applies the redhat class
    }
  default: { fail("Operating System ${::operatingsystem} not supported") }
}

or (demonstrating different package names and configs to use). Note - kernel variable has come from facter (check facter output for examples of what can be used

case $::osfamily {
  'redhat' : {
    $package_name = 'redhat'
    $nginx_package_name = 'nginx'
  }
  'debian' : {
    $package_name = 'debian'
    $nginx_package_name = 'nginx'
  }
  'windows' : {
    $package_name = 'windows'
    $nginx_package_name = 'nginx-service'
  }
  default : {
    fail( "Unsupported osfamily ${::osfamily}")
  }
}
package {'nginx':
  ensure => present,
  name => $nginx_package_name
}
file {'/var/www':
  ensure => directory,
  name   => '/var/www',
}
file {'index.html':
  ensure => file,
  name   => '/var/www/index.html',
  source => 'puppet:///modules/nginx/index.html',
}
file {'default.conf':
  ensure  => present,
  name    => '/etc/nginx/conf.d/default.conf',
  source  => "puppet:///modules/nginx/default-${::kernel}.conf",
  require => Package['nginx']
}
file {'nginx.conf':
  ensure  => present,
  name    => '/etc/nginx/nginx.conf',
  source  => "puppet:///modules/nginx/${package_name}.conf",
  require => Package['nginx']
}
if / elsif / else Statements

Pretty standard flow, just new syntax to get used to

if $server != 'fileserver01' and $role != 'filesharing' {
  file {'/etc/smb.conf': ensure => absent}
  }
  else {
  file {'/etc/smb.conf': ensure => present}
  }

Kinda simplistic example, but it demonstrates the basic if/else workflow.

Operators
  • boolean
  • and, or, not
  • Comparison
  • ==, !=, =~, <, >, <=, >=
  • Arithmetic
  • +, -, *, /, >>, <<
  • Membership
  • in

Precedence is

  • ! (not)
  • *, / (divide)
  • -, - (plus and minus)
  • <<, >> (left shift and right shift)
  • ==, !=, =~ (Equal, Not equal, Regex equal)
  • =, <=, <, > (greater than equal, less than equal, greater than, less than)

  • and
  • or

Parentheses can be used to manually override/set precedence

Embedded Ruby (ERB) Templates
  • Templates are generally text files
  • Using ERB tags allows you to
  • display and act on interpreted variable values
  • Modify the flow of logic
  • Execute ruby code for calculations or iterations
  • Templates reside in the {module}/templates directory
Example
root@robbie:~/puppetcode/modules # cat nginx/templates/index.html.erb
<HEAD>
  <TITLE> Test Web Page for <%= @kernel %> platform</TITLE>
</HEAD>
<BODY>
  This is a test web page for the <%= @kernel %> Platform
  Added some additional content
</BODY>
root@robbie:~/puppetcode/modules # cat ./nginx/manifests/init.pp
file {'index.html':
  ensure => file,
  name   => '/var/www/index.html',
  content => template('nginx/index.html.erb'),
}
Classes vs Defined Resource Types

Classes can have 1 instance
DRTs can have infinite (i.e. many) instances

define nginx::vhost () {
notify { "this is generating the vhost ${title}" }
# ... additional code here
}

nginx::vhost { "www.puppettest.com":
  ensure => present
}
Simple Example - nginx

project dir: /home/robbie/puppetcode/
git dir: /home/robbie/puppetcode/.git

  • modules (i.e. an nginx module, a users module, etc) reside in a subdir in here
  • this example looks specifically at the nginx module
  • The permissions are wrapped up in the "File" resource default, rather than repeating them in every file declaration.
  • Note the capital "F".

Below is some sample code.

root@robbie:~/puppetcode # cat ./modules/nginx/manifests/init.pp
class nginx {
  notify {'begin':
    message => 'Commencing installation of nginx',
  }
  package {'nginx':
    ensure => present,
  }
    File {
    owner => 'root',
    group => 'root',
    mode  => '644',
  }
  file {'/var/www':
    ensure => directory,
    name   => '/var/www',
  }
  file {'index.html':
    ensure => file,
    name   => '/var/www/index.html',
    source => 'puppet:///modules/nginx/index.html',
  }
  file {'default.conf':
    ensure => present,
    name   => '/etc/nginx/conf.d/default.conf',
    source => 'puppet:///modules/nginx/default.conf',
  }
    service {'nginx':
    require   => Package['nginx'],
    subscribe => File['index.html', 'default.conf'],
    ensure    => running,
    enable    => 'true',
  }
}

[master]root@robbie:~/puppetcode # cat ./modules/nginx/examples/init.pp
include nginx
[master]root@robbie:~/puppetcode # ll ./modules/nginx/files/
total 12
-rw-r--r-- 1 root root 1277 Aug 26 02:00 default.conf
-rw-r--r-- 1 root root  161 Aug 26 02:00 index.html
-rw-r--r-- 1 root root 1059 Aug 26 02:00 nginx.conf
[master]root@robbie:~/puppetcode #