Rspec fixtures tip: symlink to other modules in your controlrepo

If you are writing rspec tests against your controlrepo, specifically your profile module, you need to set up your .fixtures.yml file to reference the other modules in your controlrepo. For example, here’s a list of the modules in the dist directory of a controlrepo:

dist
├── eyaml
├── profile
└── role

If any of the profile modules reference one of the other modules, it needs to be referenced as a fixture or an error is generated that the resource cannot be found:

$ be rspec spec/classes/eyaml_spec.rb

profile::eyaml
  should contain Class[profile::eyaml] (FAILED - 1)
  should contain Class[eyaml] (FAILED - 2)

Failures:

  1) profile::eyaml should contain Class[profile::eyaml]
     Failure/Error: it { should create_class('profile::eyaml') }

     Puppet::PreformattedError:
       Evaluation Error: Error while evaluating a Function Call, Could not find class ::eyaml for build at /home/rnelson0/puppet/controlrepo/
dist/profile/spec/fixtures/modules/profile/manifests/eyaml.pp:15:3 on node build

We can reference these local modules with the symlinks type of fixture, like this:

fixtures:
  symlinks:
    profile: "#{source_dir}"
    eyaml: "#{source_dir}/../eyaml"

The paths must be fully-qualified, so we use #{source_dir}, a magical value available through rspec, to start with the fully-qualified path to the current directory dist/profile. Follow the symlinks type with repositories and other types of fixtures as you normally would.

When you run rake spec or rake spec_prep, these symlinks will be created underneath dist/profile/spec/fixtures/modules automatically for you. Your rspec tests on profiles that include content from these other modules will no longer error with an inability to find the module in question.

$ be rake spec_prep
$ be rspec spec/classes/eyaml_spec.rb

profile::eyaml
  should contain Class[profile::eyaml]
  should contain Class[eyaml]

Finished in 3.39 seconds (files took 1.61 seconds to load)
2 examples, 0 failures

Enjoy!

Including additional resources in your rspec-puppet tests

I’m a strong advocate of creating unit tests for your puppet code with rspec-puppet. I’ve written a number of articles on tests before, so here’s one more.

When you’re testing a class, sometimes there’s an expectation that it’s used alongside another class or resource. But your test is only against your class, so how do you add the other resource to the catalog? As an example, let’s look at the module jlambert121/puppet and how some of the subclasses in the module are tested, specifically puppet::server::install. The top level describe statement ensures that the class puppet::server::install is present. It does not ensure that some of the other classes it relies upon are present.

This is done through the : :pre_condition statements at the beginning of each 2nd level describe, starting on line 3:

  let(:pre_condition) { 'class {"::puppet": server => true}' }

Other describe blocks instantiate the class with different parameters, as needed for the individual test.

In the : :pre_condition, you can add any Puppet DSL you want. I would not suggest going overboard, but including or instantiating another class or a small number of resources can be very helpful. This can be especially helpful with profile classes, where you may want to test behavior when another profile is present, e.g. testing an application profile with multiple webserver profiles.

I did not find the : :pre_condition statement documented anywhere in the rspec-puppet docs, so I hope this helps others discover it.

Puppet Enterprise Migration from 3.8.4 to 2015.3.3

I recently completed a PE migration from 3.8.4 to 2015.3.3 (puppetserver 2.2.41 and puppet agent 4.3.2). This was a somewhat painful exercise, as we kept running into issues because we had gotten so far behind on upgrades. If you need to perform the same kind of upgrade, I hope this broad-stroke description of the upgrade steps will help you. Before we get to the upgrade, let’s cover some of the pre-requisites.

Release Notes

Always read the release notes first. I am sure I will cover some of the notes below in specific problems we ran into, but there’s a lot on there that we did NOT encounter.

Future Parser

When you get to PE 2015.3.3, you’ll be running Puppet 4. Make sure you have the future parser enabled on your master or agents, by following these instructions. You’ll likely run into at least one issue if you weren’t doing this before. For example, automatic string/array conversions may not work as you expect. Get your code up to par before moving forward.

Continue reading

Setting up modulesync with multiple Puppet modules

If you maintain more than one Puppet module, you’ve probably spent some time aligning changes in your general setup by hand – rspec helpers, Gemfile, Rakefile, your travis config, etc. Once you have a third or a fourth module, you find that does not scale. Thankfully, there’s a great tool to help automate this: Modulesync.

How It Works

We’ll discuss how modulesync works first, so that we understand how to set it up. For instance, to perform a dry run, you would run msync update –noop in a modulesync configuration repo. This creates a directory modules and clones the managed modules into that directory. For each module, it then describes the diffs between the existing contents of the module and the config defined by the modulesync configuration repo. Once the changes are reviewed for accuracy, run msync update -m “Commit message here” and the diffs are applied to the default branch (master) with the commit message specified. By creating a modulesync.yml file, the default namespace and branch can be specified. The use of a different branch name allows you to create a PR to apply the changes.

Continue reading

Parallelized Rspec Tests

Peter Souter showed me a recent PR for the puppet-approved jenkins module where he parallelized the rspec tests. When there are a large number of tests in separate files, it can take a lot of time when run in series. Parallelizing the tests MAY offer a speed improvement; in Peter’s case, it reduced the time by almost 50%. With a small number of tests, or when an overwhelming percentage of the tests are in a single file, there may be no benefit or even a decrease in performance, so be sure to test out its effects before committing to it. It’s a pretty simple change, but let’s look at it in some detail anyway.

Gemfile

In your Gemfile, you need to add one line:

gem 'parallel_tests'

Continue reading

Running rspec-puppet tests with granularity

When working on your puppet code, you’re going to want to run rspec against your tests on a regular basis. There are a few quirks to this process that we should cover quickly.

“Normal” Usage

Let’s start with a simple test of everything. You can do this with bundle exec rake spec (or the test target, which includes spec plus some other targets). That would look something like this (note: be is an alias for bundle exec):

Continue reading

Ruby net/https debugging and modern protocols

I ran into a fun problem recently with Zabbix and the zabbixapi gem. During puppet runs, each puppetdb record for a Zabbix_host resource is pushed through the zabbixapi, to create or update the host in the Zabbix system. When this happened, an interesting error crops up:

Error: /Stage[main]/Zabbix::Resources::Web/Zabbix_host[kickstart.example.com]: Could not evaluate: SSL_connect SYSCALL returned=5 errno=0 state=SSLv2/v3 read server hello A

If you google for that, you’ll find a lot of different errors and causes described across a host of systems. Puppet itself is one of those systems, but it’s not the only one. All of the systems have something in common: Ruby. What they rarely have is actual resolution, though. Possible causes include time out of sync between nodes, errors with the certificates and stores on the client or server side, and of course a bunch of “it works now!” with no explanation what changed. To confuse matters even more, the Zabbix web interface works just fine in the latest browsers, so the SSL issue seems restricted to zabbixapi.

To find the cause, we looked at recent changes. The apache SSLProtocols were changed recently, which shows up in a previous puppet run’s output:

Continue reading

Puppet 4 Lessons Learned

I’ve been working recently on migrating to Puppet 4. All the modules I maintain have supported it for a little bit but my master and controlrepo were still on Puppet 3. I slowly hacked at this over the past month and a half when time presented itself and I learned a few things. This post is an assortment of lessons learned, more than a tutorial, but hopefully it will help others going through this effort themselves.

At a high level the process consists of:

  • Make sure your code is Puppet 4 compatible via Continuous Integration.
  • Find a module that manages Puppet 4 masters and agents. If your current module works with 4, this step is much easier.
  • Build a new template or base image that runs Puppet 4.
  • Update your controlrepo to work with a Puppet 4 master, preferably with the new puppetserver instead of apache+passenger, again using CI for testing.
  • Deploy the new master and then start adding agents.

The biggest lesson I can give you is to perform these changes as 5 separate steps! I combined the last three into a single step and I paid for it. I know better and my shortcut didn’t turn out so well. Especially, do not take your Puppet 3 master down until your Puppet 4 master build tests out okay! Alright, let’s go through some of the steps.

Continue reading

Kickstart your CentOS Template, EL7 Edition

I wrote an article on kickstarting your CentOS Template in early 2014 that focused on Enterprise Linux 6. Later in the year, RHEL7 was announced and CentOS 7 soon followed. It’s well past time to refresh the kickstart article. To keep this more of a “moving target”, I’ve created a github repo to host the kickstart files at puppetinabox/centos-kickstart, so you can turn there for updates or submit your own PRs. I’m also toying with an existing puppet module danzilio/kickstart that generates kickstart files, and I plan to contribute some PRs to it to manage the kickstart service itself. In the meantime, I’ll show a small profile that will do the same thing, since it’s just apache and a few files.

Kickstart Configuration

The new EL7 file was based off the EL6 version. I simply changed the package list as some were no longer available and the open-vm-tools are now the preferred method of VMware tools management. That section was removed from the bottom. In the additional steps section, I changed the yum repo for puppet from Puppet 3 to Puppet Collections 1 for Puppet 4. I also removed the banner setup, that’s easy enough to add in if you like.

Kickstart Service Management

The kickstart service itself is pretty simple. You can use puppetlabs-apache to install apache and then place your files in it’s default root of /var/www/html. Take the kickstart files and add them to dist/profile/files with any modifications you require. Then create a profile that includes apache plus the kickstart files. That would look something like this:

Continue reading

Minecraft module for Puppet

At PuppetConf, I had the pleasure of meeting Bren Briggs, who I knew from twitter and IRC, so I was pretty happy when he asked me if I wanted to work on a Minecraft module with him. Of course we’re busy with life and work and the holidays, so we haven’t started yet, but we’re going to try soon!

Bren floated some ideas past me and one of the big questions was, do we want to deploy the Minecraft instance as if we’re on bare metal or via Docker? He started a twitter poll, but those things only last 24 hours and we only received 3 votes. If you were going to use a Minecraft module, would you want one that uses Docker or one that does not?

I’m a little biased here, but I hope people want to see Docker. I’ve not used Docker in anger before, and I need more motivation to update my kickstart setup from EL6 to EL7. But don’t let me sway you, let us know what YOU would like to see. Thanks!