Validating Config Files in Puppet and Ansible

While doing some experiments with Ansible I came across a little snippet of code that I really liked -

  - name: manage /etc/sudoers
    template: src=sudoers.j2 dest=/etc/sudoers validate='visudo -cf %s'

Ansible runs the command specified by ‘validate’ against the expanded templates contents and only copies the newly generated file in to place if it’s valid. This is a wonderful feature that will help stop you from making some potentially time consuming errors.

While this is a lovely thing for Ansible users I use a lot of Puppet at work and was curious as to what the easiest to implement equivalent was. So I went for a rummage inside the Puppet stdlib module. This module is a grab bag of short functions, including thinly wrapped ruby methods and data validators. At the bottom of the readme I found validate_cmd , which seems like a good fit. So, let’s install the stdlib module and create a small stub sudo module for testing (note - don’t write your own sudo module - use saz/sudo instead.)

$ sudo puppet module install puppetlabs/stdlib

$ mkdir -p sudo/manifests

$ cat << 'EOC' > sudo/manifests/init.pp
class sudo {

  file { '/etc/sudoers':
    ensure  => 'file',
    content => template('sudo/sudoers.erb'),
  }

}

The above code creates a single file resource, the content of which comes from a template that we might occasionally get wrong. Let’s change the module to incorporate the validate_cmd function -

$ cat << 'EOC' > sudo/manifests/init.pp
class sudo {

  $sudoers_content = template('sudo/sudoers.erb')

  validate_cmd($sudoers_content, '/usr/sbin/visudo -c -f', 'Visudo validate failed')

  file { '/etc/sudoers':
    ensure  => 'file',
    content => $sudoers_content,
  }

}

In this revision we expand the templates content and assign it to the $sudoers_content variable so we can later use it in both the validate_cmd and the file resource itself. While this may not seem like a massive change to the class adding that extra indirection and function call for each resource you want to validate in this way can quickly become both ugly and tiresome so kudos to the Ansible approach.

If we run puppet with an invalid template we see the following output:

Error: Visudo validate failed
Execution of '/usr/sbin/visudo -c -f /tmp/validate_cmd20131009-18575-1gnhhbt-0' returned 1: visudo: >>> /tmp/validate_cmd20131009-18575-1gnhhbt-0: syntax error near line 0 <<<
parse error in /tmp/validate_cmd20131009-18575-1gnhhbt-0 near line 0
 at /home/.../modules/sudo/manifests/init.pp:5 on node kvm02.udlabs

and the original file is not changed, so we’ve been saved from making a costly error.

There are also a couple of important considerations to keep in mind if you use validate_cmd. First, it’s a function. Assuming you run your agents against a puppetmaster then the function is run on the puppet master, which means it may be running a different version of the validate command than the host it’s actually being deployed to. Secondly this treats the content to validate as a standalone atomic unit. If you have a config file that includes other content (such as nagios configs) then these will not be available at validate time on the master and so the validate will fail. Thirdly the version of stdlib on the forge is currently (partially) broken when it comes to validate_cmd. There is a small race condition where sometimes the validate is run before the tempfile is actually written to disk and so an empty file is validated. This is fixed in upstream github and should be released any day now.

So, for this use case I think Ansible gets 1 point and Puppet gets 0.5.