adrift on a cosmic ocean

Writings on various topics (mostly technical) from Oliver Hookins and Angela Collins. We have lived in Berlin since 2009, have two kids, and have far too little time to really justify having a blog.

Puppet class API verification and testing

Posted by Oliver on the 20th of January, 2011 in category Tech
Tagged with: APIcontinuous integrationhackspuppetruby

I've been working recently on building up a more reliable, predictable Puppet infrastructure. I'm not a developer at heart, but I've been trying very hard to apply software development principles to what I do, since generally configuration management and software deployment is becoming more and more just another software problem. One of the end goals of the system I am building is to have a pretty GUI (isn't that always the end goal?) that will let you know before you deploy something what kind of information it requires.

Applying the concept of encapsulation to our Puppet code, we are trying to keep all the details of each module relatively hidden from the outside so that you don't need to look at the Puppet code to use it - not exactly revolutionary stuff. All that should matter is the interface to the code - and we're going down the path of Puppet 2.6 parametrised classes here. So, you have a bunch of parametrised classes and you want to see what the interface is, or in my example, have some GUI figure out what variables you need to pass to the class in order to use it. I know many software tools already do this, but I have no idea exactly how they do it (although it is probably just a variation on the standard code parsing phase).

Armed with this information we can also do verification on the interface to a module, and see when it changes. Dan Bode and Luke Kanies of Puppet Labs helped me out with the following code.

Let's set up a very basic class in a file:

# test API - test.pp
class test ($foobar) {
    file { '/tmp/foo':
        ensure => present,
        content => $foobar;

Now we call Puppet directly from Ruby on this file, and render the class as PSON (although it could just as easily be YAML, or probably other formats):

#!/usr/bin/env ruby
require 'rubygems'
require 'puppet'
puts Puppet::Resource::Type.find('test').render(:pson)

And here's the output:

{"name":"test","arguments":{"foobar":null},"line":2,"doc":"test API - test.ppn","code":"[File["/tmp/foo"]]","type":"hostclass","file":"/home/ohookins/test.pp"}

This is ridiculously easy to use later to pretty-print our class interface or pre-load HTML forms etc etc. If we change that variable to have a pre-set default value, you can easily see the change in output PSON:

{"name":"test","arguments":{"foobar":""mydefault""},"line":2,"doc":"test API - test.ppn","code":"[File["/tmp/foo"]]","type":"hostclass","file":"/home/ohookins/test.pp"}

What is even cooler is that comments preceding the class definition are parsed and included as a doc pair in the hash, which could have any number of cool uses. I assume this code would work equally well for printing interesting information about defined types or unparametrised classes (although in that case I'm not sure there would be as much value). Let me know if this helps you at all, or if you find more cool uses for it!

© 2010-2018 Oliver Hookins and Angela Collins