Reusing Puppets Package providers

One of puppets more under-appreciated features is its ability to abstract and smooth the edges of certain operating system tasks and behaviours. Even something as trivial as installing a package can actually become a portability nightmare once you consider the number of different systems in the wild - rpm, yum, dpkg, pkgsrc etc. - and the varied commands needed to use them. You end up either hard coding commands, and sacrificing portability, or writing your own detection, lookup and invocation logic.

That sounds like, dull, scut work so how does puppet deal with this? And how can we reuse this work to simplify our own code? In slightly simplified terms, Puppet has a package type, which is backed by a number of providers. Each of these providers actually implement the required functionality for a given package manager and contains all the code we need. So how do we harness this existing work? Quite easily. Luckily for us, puppets providers are written in ruby code and are simple to call in our own scripts:

# show package version
$ irb

irb(main):001:0> require 'puppet'
=> true

irb(main):002:0> Puppet::Type.type(:package).new(:name => "bash").provider.properties
=> { :provider=>:yum, :ensure=>"4.1.7-3.fc14", :release=>"3.fc14",
=>   :arch=>"i686", :epoch=>"0", :name=>"bash", :version=>"4.1.7" }

# do the same thing with an explicitly specified provider.
irb(main):003:0> Puppet::Type.type(:package).new(:name => "bash", :provider => "rpm").provider.properties
=> { :provider=>:rpm, :ensure=>"4.1.7-3.fc14", :release=>"3.fc14",
     :arch=>"i686", :epoch=>"0", :name=>"bash", :version=>"4.1.7" }

While that snippet will hopefully whet your appetite if you need a more worked example I’ve put a small Puppet Package Provider wrapper up on github. The script will enable you to do the basic install, update and delete without knowing or caring what the underlying package manager is. Hopefully these little code snippets will help you stop thinking of puppet as “just” a tool and show how parts of its code base can be used as a framework to improve other parts of your tool chain.

As an aside it’s also worth mentioning that you can globally Change the Package provider in puppet if you’re not happy with its auto-detection.