Skip to main content


She would never forget the moment she’d finally realised. It was a Saturday morning, a few months ago, sunny for once. They’d slept in, tired from the week before. He, with a job in the city, working late on some project, paying the bills, keeping the bosses happy, that sort of thing. She, still on maternity leave, working out how to care for this beautiful, screaming child, her first. Their first. He set the table for breakfast whilst she fetched milk, cereal, toast, black coffee. They always tried to make a bit more of an effort on weekends; a chance to slow the pace.

He’d not been himself, recently. She’d noticed gradually, like the leak in the floor above; not really noticeable at first, something not quite right, then water starting to seep through. She’d put it down to a bad week, then a bad month. They both seemed tired all the time, could never quite get the rest they needed. Things will improve when we’re not being woken every few hours, she thought. Just give him space. But the days turned into weeks, and she began to really worry. Mid-week, she’d been on the phone to a friend, confiding she didn’t know what was happening, what to do. To her surprise, her friend, although clearly concerned, sounded cautious—guarded, almost—as if there were something not being voiced. After putting down the phone, she stared at it in bemusement. And then it hit her. Oh god, he’s having an affair.

She didn’t get much sleep, that night. She lay next to him, staring up at the ceiling. The streetlamp directly outside the window leaked around the edges of the curtain, mixing the shadows with a pool of orange. She couldn’t cry, not at first, but as the hours drew on, the pain in the front of her head started to shift, and tears started to drip noiselessly down her face. Across her cheek, onto the pillow, each tear slightly wetter than the last. Like the ceiling. She fell into some sort of half-awake, half-asleep doze, somehow still aware of the streetlamp outside.

In the morning, she knew what she was going to do. She’d wait until he came home from work that evening, and then confront him. Tell him she knew. Demand that he explain himself. But she suddenly knew she couldn’t wait until evening. He came out of the shower, towel still wrapped around him, about to get dressed for work. He seemed a little surprised to see her in the hallway, leaning against a doorframe for support. Goodness knows what look she had on her face. With nothing like the self-assurance she’d been planning, she somehow found a voice. It didn’t sound like hers; it was smaller and much further away.

He was late for work, that day. At first he’d seemed shocked, then that quickly morphed into anger. That helped her confidence, and she realised that she was angry, too. They both shouted at the same time, standing there in the cramped hallway. The initial fuel burned out, and some listening mixed with the shouting. He swore blind there was no one else. He couldn’t believe she’d even thought it. Didn’t he always come home soon after work? When would he have the time for an affair, anyway? She’d never really known him to lie to her. She was glad to have her suspicions shot to shreds, not least because the more she believed him, the more the dark, drowning sense of guilt grew and grew and strangled her. Yes, she believed him. How could she have even thought such a thing? Where had that thought even come from? How had she even said that? She hated herself.

But all that had been a few days before. They hadn’t exactly said much to each other since, but she was hopeful things would smooth over. Some time together this weekend was what they needed. A chance to relearn how to trust each other. A chance for the immediate rawness in both their minds to fade. They were both just tired, after all. Perhaps work had been tougher than he’d been saying. She poured them each a coffee, and carefully picked up her child out of the cot. They took their place opposite him at the table.

Her child looked so beautiful, so trusting; it made her feel full of love. As she gazed, she was sure she saw what had to be two smiles, first at her, then at him. Her eyes brimmed with tears, but the happy kind. Things would work out; how could they not? She looked at him, expecting their eyes to meet as they shared that unspoken moment. But he hadn’t seen; he was reading something or other on his phone.

The second realisation was even worse than the first, and more permanent. As she sat there, locked in a toxic bubble of thoughts, she felt the life drain out of her. She suddenly couldn’t quite remember how to breathe. There seemed to be something stuck in her throat, strangling her from inside. The pain returned to the front of her head. She struggled against it, feeling her mind forcibly turning to look at some unspeakable horror she could see growing in her peripheral. The swirling of particles taking form. The electrified split-second before lightning strikes.

She’d somehow lost him.

puppet modules using librarian-puppet

Using Librarian-puppet, it is possible to extensively clean up a vendored Puppet config when versioning the manifests. Much like Ruby Bundler, dependencies can be specified and locked, with the lock file checked into the VCS. This is arguably more straightforward than maintaining module dependencies as Git submodules. If using an approach similar to puppet master git repository, the Git hook can be updated to satisfy the modules immediately prior to triggering the Puppet run. Not only does this reduce the complexity of a repository compared to vendoring modules, it makes upgrading individual modules much easier.

Throughout, an OS of Ubuntu Server 14.04 LTS is assumed; instructions for other environments should be similar.

installing librarian-puppet on puppet master

The Ubuntu Server librarian-puppet package wasn’t up-to-date enough for me, so I installed from the gem. For servers running Ruby for an application, I tend to satisfy this dependency independently to that needed for system packages. Thus, I installed Librarian-puppet on the Puppet Master using the system method.

apt-get update
apt-get install build-essential ruby-dev
gem install librarian-puppet

Update whichever method you use to prepare and trigger Puppet runs. If using the method outlined in puppet master git repository, update the post-receive Git hook to be something like:

# /etc/puppet.git/hooks/post-receive


set -e

GIT_WORK_TREE=/etc/puppet git checkout -f

cd /etc/puppet && librarian-puppet install

sudo puppet agent --test

installing librarian-puppet in repository

Presuming your Puppet repository is set up for Ruby Bundler, install Librarian-puppet:

# Gemfile

gem 'librarian-puppet'

Librarian-puppet takes control of the modules/ directory, and the recommendation is to clear this entirely and specify everything as a module dependency. I typically have at least one internal module, however, and maintaining this as a separate repository feels like a complication. Thus, I clear all apart from the internal modules, and continue to contain those directly within the repository. Despite not being the recommended approach, it seems to work well enough.

# clear unwanted modules
librarian-puppet init

If maintaining some internal modules, modify the Librarian-puppet .gitignore generated, to continue to track /modules/. Just remember to be careful to not check in modules managed by Librarian-puppet.

Specify the module dependencies, e.g.

# Puppetfile

mod 'puppetlabs-apt'
mod 'puppetlabs-firewall'
mod 'puppetlabs-ntp'
mod 'puppetlabs-stdlib'

After changing Puppetfile, update Puppetfile.lock by running

librarian-puppet install

If not developing locally using a full environment with access to Puppet and such, this might error. However, the dependencies should be resolved and the lock file updated nonetheless.

All that remains is to commit and trigger your Puppet run as usual, e.g. using git push. If all is well, Puppet Master /etc/puppet/modules/ should become populated with the dependencies specified in Puppetfile, in addition to any internal modules checked in to the repository. To upgrade a module, just locally change Puppetfile and rerun librarian-puppet install, or use librarian-puppet update.

tiredpixel ☮

puppet master git repository

Setting up a Puppet Master to deploy from a Git repository without GitHub or Bitbucket or similar is straightforward, thanks to the flexibility of Git hooks. Versioning manifests for Puppet or alternatives such as Chef is an excellent idea, allowing the benefits of a VCS to be leveraged to provide a centralised comprehensive historical trail even when working with multiple devops. One good approach is to host the repository using external service GitHub or Bitbucket or similar, setting appropriate access permissions and using Capistrano or similar to deploy and trigger a Puppet run. This has the disadvantage of placing a large amount of trust in the VCS service, however. An alternative would be to run your own repository server internally; however, that seems overkill for a single-repository case. A much lighter solution is to simply use Git over SSH and host the repository directly on your Puppet Master.

Throughout, an OS of Ubuntu Server 14.04 LTS is assumed; instructions for other environments should be similar.

versioning the puppet config

If you are already running a Puppet Master with a versioned config, you can likely use the same one with minimal changes. If setting up a fresh Puppet Master, you might like to install Puppet Master using your choice of packages, and then version the resultant configs. e.g.

Create (or reuse) a repository from the contents of /etc/puppet.

creating the bare git repository

Create a bare Git repository, such as is suitable for receiving pushes. It’s better to use a bare repository rather than one with a working copy, to ensure conflicts are not run into. Ensure that it and the target /etc/puppet are owned by the user through which you’ll be connecting, which should be able to sudo without password. If such a user is called ubuntu:

Create a post-receive hook in the bare Git repository, to update /etc/puppet and trigger a Puppet run. As ubuntu or similar user:

triggering the first puppet run

If using fresh Git repository, ensure puppet.conf is checked in and set with appropriate settings (dns_alt_names, server, etc.). At this point, the Puppet Master should be set up and running.

Set the Git remote, and push the Git repository, which should automatically update the contents of /etc/puppet, trigger a Puppet run, and report the results directly to the console:

Thereafter, git push should trigger a Puppet run, without requiring the repository to be hosted externally or a deployment mechanism invoked.

Clearly, this isn’t that involved. But that’s the point; it’s easy to set up and maintain, and requires very few moving parts. Very little of this is Puppet-specific (the approach indeed being similar to that used for years to deploy all sorts of things), so it should be straightforward to adapt it for use with other automation tools.

tiredpixel ☮