You can test your custom Ohai plugins with ChefSpec using the chefspec-ohai gem, and with InSpec in Test Kitchen. This frees you from the traditionally difficult task of debugging a failing Ohai plugin.
In this post, I’ll focus solely on testing an Ohai plugin. If you are interested in a complete tutorial that walks through the process of creating a cookbook with the plugin and all the tests, check out the Learn Chef tutorial for building an Ohai plugin, or watch the embedded video below:
Ohai collects data about your system. The data is captured through various plugins that are defined and shipped with Ohai. Additionally, it supports the ability to load custom plugins that you define.
Testing those plugins has always been hard. To ensure the plugin works would require you to deliver it to your target node and then execute ohai
or chef-client
.
No output? Why doesn’t the run fail?
When an Ohai plugin fails, it does so silently, so that it does not effect the remainder of the ohai
execution and the chef-client
run. If you were to make a mistake while creating or changing the plugin it would take a long time to troubleshoot the issue.
I have taught numerous chefs how to create Ohai plugins in our Chef Intermediate course. When errors happen, and they sure do happen, we pair up and try to play the game “spot-the-typo”. Hardly a fun game to play!
An ohai plugin is usually a file within your cookbook that you deliver to your node with the ohai cookbook and then properly loaded with the chef-client cookbook.
Here is an example plugin that captures the state of the modules installed with Apache:
# cookbooks/apache/files/default/apache_modules.rb Ohai.plugin :Apache do provides 'apache/modules' collect_data :default do apache(Mash.new) modules_cmd = shell_out('apachectl -t -D DUMP_MODULES') apache[:modules] = modules_cmd.stdout end end
Defining a ChefSpec test will allow us to verify our syntax and logic defined in the plugin.
A new gem contains the necessary helpers to load and unit test the Ohai plugin. Here, we will install it and then require it within our common test helpers files. Then, we will define a new test that we will execute.
$ chef gem install chefspec-ohai
# cookbooks/apache/spec/spec_helper.rb require 'chefspec' require 'chefspec/berkshelf' require 'chefspec/ohai'
# cookbooks/apache/spec/ohai_plugins/apache_modules_spec.rb require 'spec_helper' describe_ohai_plugin :Apache do let(:plugin_file) { 'files/default/apache_modules.rb' } it 'provides apache/modules' do expect(plugin).to provides_attribute('apache/modules') end let(:command) { double('Fake Command',stdout: 'OUTPUT') } it 'correctly captures output' do allow(plugin).to_receive(:shell_out).with('apachectl -t -D DUMP_MODULES').and_return(command) expect(plugin_attribute('apache/modules')).to eq('OUTPUT') end end
Let’s talk about the syntax of an Ohai plugin test:
describe_ohai_plugin
is a new example group alias that allows you to easily test Ohai pluginslet(:plugin_file) { ... }
defines the relative path to the cookbook file that contains the Ohai pluginlet(:command) { ... }
returns the test double command object which we use in the next exampledouble('Fake Command',stdout: 'OUTPUT')
is an RSpec test double that takes a name and a hash of parameters. These parameters are converted to methods on the double object.allow(plugin).to_receive(:shell_out).with('...')
is RSpec’s way of allowing messages to override the shell_out command when used within the plugin; replace the results of it when given the specified command.plugin_attribute('apache/modules')
retrieves the value stored in the modules key (Similar to writing node['apache']['modules']
)$ chef exec rspec spec/ohai_plugins/apache_modules_spec.rb .[2016-12-02T14:18:59-06:00] WARN: Plugin Definition Error: <files/default>: collect_data already defined on platform default . Finished in 0.20469 seconds (files took 1.54 seconds to load) 2 examples, 0 failures
Testing our plugin with ChefSpec allows us to ensure the plugin is defined correctly. It does not verify if the command we wrote works correctly on our given platform / platform version. This is where we can use Test Kitchen.
The ohai community cookbook provides ohai_plugin
to assist us with deploying the plugin. Here, we will establish a dependency on the ohai cookbook, create a new recipe, define a Test Kitchen suite, create and execute a new InSpec test.
# cookbooks/apache/metadata.rb # ... rest of your cookbook metadata ... depends 'ohai'
# cookbooks/apache/recipes/ohai_apache_modules.rb include_recipe 'apache::default' # the plugin requires the software to be installed ohai_plugin 'apache_modules'
# cookbooks/apache/.kitchen.yml # ... REST OF KITCHEN CONFIGURATION ... suites: # ... other test suites ... - name: ohai_apache_modules run_list: - recipe[apache::ohai_apache_modules] attributes:
$ mkdir -p test/recipes/ohai_apache_modules
# cookbooks/apache/test/recipes/ohai_apache_modules/default_test.rb plugin_directory = '/tmp/kitchen/ohai/plugins' describe command("ohai -d #{plugin_directory} apache") do its(:stdout) { should match(/core_module/) } end
$ chef exec kitchen verify ohai_apache_modules
Now you can build your Ohai plugins with tests. ChefSpec will verify that you wrote the plugin correctly. InSpec will verify that the command really works on your specified platform. These feedback mechanisms will ensure you maintain your development velocity and are much more fun than playing “spot-the-typo”.