Managing Puppet modules with librarian-puppet

Puppet Labs logoOnce you have your Infrastructure defined as code you need to use coding best practises.

This definitely applies to Puppet code, you have Puppet classes to encapsulate functionality, and modules that group classes together for reuse and redistribution. Modules can be reused from git repositories or from the Puppet Forge, a web archive of tarballed modules in the same fashion as rpm or maven repositories. Modules in the forge are defined by a provider, module name and version, much like Maven groupid, artifactid and version, and a provider has full control over the modules that can be published under its name, with a more open approach, as anybody can upload anything themselves through the forge website, just signup and start uploading them.

But trouble starts when you are using a number of modules from different places, as each module can have its own set of dependencies. How can the dependencies be defined, downloaded and installed automatically as happens with any other package manager?

There are several options actually

  • Git submodules. This is the poor man’s version, where you add the git repositories of other modules as git submodules and use git submodule init and update commands to stay up to date
  • Puppet module tool. Allows the installation of modules from the Puppet forge based on the module name provider/name and version. It will download and unpackage the module in the directory specified
  • Librarian-puppet. It is an extension of ruby’s bundler model to install gems, having a file that defines your dependencies, Gemfile in the case of bundler, Puppetfile in the case of librarian, and a .lock file with the resolved dependencies.

The resolution model is different than those of yum, apt-get or maven, that resolve dependencies at every run on the client, relying on the clients to consistently do that over versions and machines. In the bundler or librarian model, resolution only happens once and it’s saved to the lock file, which is used from there on by the clients until dependencies are changed.

Dependency declaration

The librarian Puppetfile allows defining module dependencies from several sources

Puppet Forge

Using the forge provider and module name, and optionally version it calls puppet module tool to fetch the tarball and extract it in the designed directory

mod "maestrodev/maven", "1.0.0"


Modules can also be cloned from a git repository optionally defining what branch, tag or revision to checkout

mod "maven",
  :git => "",
  :ref => 'v1.0.0'

Path folders

Useful mostly for testing or while transitioning to a full dependency model, local paths can be used as modules

mod 'mymodule', :path => './private/mymodule'

Improvements and issues

Librarian offers several improvements over the puppet module tool. Besides being able to bring in modules from different sources, librarian locks the versions of modules used, which is a must in order to consistently reproduce results. Let’s say you define a dependency on module maestrodev/maven > 1.0.0. If you run it now then librarian will lock the version to 1.0.0. If 1.1.0 is released later your dependencies will stay the same. If you try to run the puppet module tool with

puppet module install maestrodev-maven

you would end with 1.0.0 now and  a newer version later on.

There are some problems, and we hit (and fixed!) a lot of them. We have a fork of the original project where we keep updating the maestrodev branch while our pull requests don’t make it to the original repo. Unfortunately some of the patches have been sitting there for too long and we went ahead and created the librarian-puppet-maestrodev gem  that you can use instead of the original one.

Some of the issues you’ll likely find are the mismatch between puppet module version conventions and gem versions. librarian uses the bundler gemfile resolution mechanism which does not accept some versions such as the ones including dashes 1.0-rc1, or the puppet module dependency type 1.x which our librarian gem will adequately convert to ~>1.0 instead of failing. Check out the rubygems version policies for more details.

Next post I’ll cover how to use librarian in combination with puppetlabs spec helper to make your puppet testing much easier and consistent, and I’ll cover the topic in my upcoming talk on continuous delivery at ApacheCon Portland in February, plus you’ll find me next monday the 28th at the Los Angeles DevOps meetup, and at DevOps Days LA February 22nd as part of the SCALE conference.

14 thoughts on “Managing Puppet modules with librarian-puppet

  1. Pingback: Testing your puppet modules | Carlos Sanchez's Weblog

  2. Pingback: Puppet – Vagrant : Smarter / Better / Stronger | Don't Make the Same Mistake Twice

  3. Excellent blog. Do you still prefer the librarian-puppet-maestrodev gem over the librarian-puppet gem ? or have your pull-requests made it through ?

  4. How is using Git submodules the ‘poor man’s version’? It’s stable, versioned, easy, requires only the use of git, a tool you probably already have. I like the post and your fork of the tool, but I don’t understand the disregard for using git submodules.

    • with git submodules updating all submodules to the latest released version (not the latest commit!) is impossible, updating to the latest requires going into each submodule to do a pull (vs. librarian-puppet update), impossible to handle version ranges (1.x), etc…

      • Yeah, you do have to manage each plugin individually; but when it comes to Puppet this is what I would prefer. Automatic changes to my machine’s configurations is touchy. Librarian-puppet is handy, but I think potentially dangerous; and clearly not stable yet, else I’d certainly use it.

  5. Pingback: New release of librarian puppet | Carlos Sanchez's Weblog

  6. Pingback: Announcing librarian-puppet 1.0.0 | Carlos Sanchez's Weblog

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s