Category Archives: Uncategorized

test-kitchen config hijinks part 2

In part one we looked at how to share chef zero data across multiple cookbooks by creating a central repository and using environment variables to reference it.

In part two we look at how to share the config file in its entirety. This isn’t chef specific like part 1, there is chef cookbook content (setting a variable of cookbook name), but overall its easily ignored if not pertinent to you.

In part one we put essentially pointers back to the common location for all of our chef zero bits, but you have to put those pointers in every kitchen config for all the cookbooks.

Step 1, we need a default kitchen config.

In my case most kitchen configs were nearly identical, they came out of chef code generation (as ruby templates) and the cookbook name was poked into a couple places and some default values set for security groups and IAM roles. So taking the most vanilla one I had I copied that into the root of the kitchen_shared repo as ‘.kitchen.yml.erb’ and worked from there. If your starting from scratch, just copy the stock config.

Now that we have a common kitchen config, its time to reference it from all the cookbooks. Templates can essentially include other templates. There are a couple ways to include template info, this is a way I found that works.

<% @cb_name = File.readlines("./metadata.rb").select { |line| line =~ /^name/ }[0].split(" ")[1].gsub("'", "") %>
<%= ERB.new(File.read("#{ENV['kitchen_shared']}/kitchen_yml.erb"), nil, nil, eoutvar='_kitchen_yml').result(binding) %>

This now becomes your default kitchen config for all current and future cookbooks. It does some ruby to dynamically figure out the cookbook name from your cookbooks `metadata.rb` file, sets that as a variable which is available to the new instance of a template generated by the `ERB.new(…)` call, from you guessed it, our common kitchen config.

Step 2, now move on to the common ‘.kitchen.yml.erb’.

Below are excerpts of config files, none of which are complete valid configs, they are just to help explain the concepts.

In your ‘.kitchen.yml.erb’ you may have the environment variable references from part 1 or it may be just a bone stock kitchen config which is fine. Here are some examples of what you can put inside the common config

Simple template syntax time, putting ruby inside <%= %> makes it display the result of said ruby code in the resulting templated output. Putting ruby inside <% %> means no output from that block. That pretty much sums it up for what we do here but there is no lack of docs about ruby templating out there if you want to know more.

If your using kitchen-ec2 to spin up your instances, its handy to tag the instances with some metadata:

driver:
  tags: { Name: "test_kitchen-<%= ENV['USER'] %>-<%= @cb_name %>" }

When you look in aws at your instances, you will end up with your kitchen instances being self descriptive with a Name tag like:

test_kitchen-bob-redis

This is a test-kitchen instance started by userid bob (on the host that created it) which is for a cookbook called ‘redis’.

Again in kitchen-ec2/aws, if you needed your instance to use a different ssh key depending if you were in your CI environment or on your laptop:

driver:
  <% if ENV['USER'] == 'jenkins' %>
  aws_ssh_key_id: jenkins-test-kitchen
  <% else %>
  aws_ssh_key_id: test-kitchen
  <% end %>

For a non-kitchen-ec2 example, lets change runlist depending on cb_name:

driver:
  suites:
  - name: <%= @cb_name %>-kitchen-1
    run_list:
      <% if @cb_name == 'elasticsearch' %>
      - recipe[elasticsearch::default_2x]
      <% elsif @cb_name == 'redis' %>
      - recipe[base_cb::default]
      - recipe[redis::test]
      <% else %>
      - recipe[<%= @cb_name %>::default]
      <% end %>

You can see we set the name of the instance to have the cookbook name in it via the cb_name variable, and depending on cookbook name we set runlists, with a fall-thru default.

Again this is chef stuff, but the message of this post is about the templating and sharing not chef specifically.

Finally how do we test our new-fangled scheme? Super easy. From the root of the cookbook where you would run your kitchen commands just feed our new 2 line kitchen config file to the ‘erb’ binary. Lets look at our last example of the suite section of the config from a cookbook named redis:

$ erb ./.kitchen.yml

suites:
  - name: redis-kitchen-1
    run_list:

      - recipe[redis::test]

Now if you look at that gap between ‘run_list:’ and ‘- recipe’ you see where the template engine pulled out the conditional stuff masked by the <% %>. There are ways to strip those lines out, but it wasn’t playing along and in the end we never see the result so I didn’t spend time on it, and kitchen doesn’t care.

So just iterate from here, whenever you need to deviate from the defaults, throw in a conditional in your shared config and check your work.

But also this lets you do things like change the chef omnibus version for every test-kitchen run from one place, no conditional, just shared code.

provisioner:
  require_chef_omnibus: 12.13.1

Never do the “now we go through all the cookbooks and change the ” dance again… after you set this up 🙂