A collection of task oriented solutions in Puppet

 

Reduce Duplicated File Attributes

Challenge

You've had enough of specifying owner, mode, group and other settings on every file resource and you want a solution with less duplication

Solution

class reduced_duplication {

  File {
    ensure => 'present',
    owner  => 'root',
    group  => 'root',
    mode   => '0644',
  }

  file { '/etc/cobbler/modules.conf':
    content => template('cobbler/modules.conf'),
  }

  file { '/etc/cobbler/dhcp.template':
    content => template('cobbler/dhcp.template'),
  }

}

Explanation

When you start using file resources in puppet you'll probably write each and every one of them out in the full long hand style shown below:

class heavily_duplicated_resources {

  file { '/etc/cobbler/modules.conf':
    ensure  => 'present',
    content => template('cobbler/modules.conf'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
  }

  file { '/etc/cobbler/dhcp.template':
    ensure  => 'present',
    content => template('cobbler/dhcp.template'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
  }

}

While there is nothing technically incorrect with this approach it can lead to ballooning manifests, a large amount of duplication between resources and easy to miss one line differences. Did both of those resources have the same permissions?

As you become more comfortable with Puppet you can start to make more use of intermediate features such as resource defaults in order to clarify and declutter your manifests. By grouping the common attributes together we can convert the previous example to this cleaner version:

class global_file_defaults {

  File {
    ensure => 'present',
    owner  => 'root',
    group  => 'root',
    mode   => '0644',
  }
  
  file { '/etc/cobbler/modules.conf':
    content => template('cobbler/modules.conf'),
  }
  
  file { '/etc/cobbler/dhcp.template':
    content => template('cobbler/dhcp.template'),
  }

  # override the permissions for this one file
  file {'/etc/cobbler/users.digest':
    source => 'puppet:///modules/cobbler/users.digest.live',
    mode   => '0660',
  }

}

While this isn't a huge saving in raw characters typed (although in longer manifests they do start to mount up) it moves all the common settings in to a single location (keeping us clear of DRY violations) and leaves only the differences between file type definitions. There is one caveat though, any declared resource of this type that doesn't override a given setting, from this point and onwards in the include chain, get the value of the default setting, which can lead to odd action from afar head scratching.

Depending on how you like to structure your resources you can condense them even further and nest all the declarations inside one resource statement:

class single_resource_dec {

  File {
    ensure => 'present',
    owner  => 'root',
    group  => 'root',
    mode   => '0644',
  }

  file {
    '/etc/cobbler/modules.conf':
      content => template('cobbler/modules.conf');
    '/etc/cobbler/dhcp.template':
      content => template('cobbler/dhcp.template');
    # override the permissions for this one file
    '/etc/cobbler/users.digest':
      source => 'puppet:///modules/cobbler/users.digest.live',
      mode   => '0660';
  }

}

It's worth pointing out that when using this form each resource is separated with a semicolon (;), not a comma (,). I personally dislike this last format. I find the ; to end each resource is often misplaced when adding new parameters and causes easy to over look errors.