Mocking data in Test Kitchen

Thumbnail

The more complex your cookbooks, the bigger the need to supply some external information to your test machines. Passing specific attributes, values of databags or secrets for testing become necessary. We will go through these use cases and show how to mock the data in this post.

Attributes

This is the easiest use case and you have probably seen it on numerous tutorials: You have a cookbook which has different execution paths depending on some attribute value. A common one would be to specify the edition of an SQL Server you want to install. While the recipe might be the same, you can manage the distinction between Express, Standard and Enterprise by just changing an attribute.

So let’s say this is our hypothetical resource:

sql_server 'Install SQL Server' do
  action :install
  edition node['sql_server']['edition']
end

By setting the sql_server/edition attribute we can influence if this is machine gets a Standard or Enterprise edition. Of course we want to test both. To do this, we can just add two suites to our Test Kitchen configuration file and supply the attribute1:

suites:
 - name: standard
   provider:
     run_list:
       - recipe[sql_server::configure]
     attributes:
       sql_server:
         edition: standard
 - name: enterprise
   provider:
     run_list:
       - recipe[sql_server::configure]
     attributes:
       sql_server:
         edition: enterprise

That’s everything needed. The Chef provisioners will automatically pull in those attributes and make them available to your cookbooks under test.

Data Bags

When it comes to central configuration, for example some values which multiple nodes need to access, Data Bags are the way to go. We could save some License Key in there:

licenses = data_bag_item('licenses', 'microsoft')

sql_server 'Install SQL Server' do
  action :install
  edition node['sql_server']['edition']
  license licenses['sql_server']
end

In Test Kitchen we can mock this easily, as well:

suites:
 - name: standard
   provisioner:
     run_list:
       - recipe[sql_server::configure]
     attributes:
       sql_server:
         edition: standard
     data_bag_path: test/integration/data_bags/ # this is actually the default location

The directory test/integration/data_bags/ 2 contains a subdirectory licenses (the data bag name) and a file microsoft.json (the item name) and this contains:

{
  "sql_server": "..."
}

The most complicated thing about this is to remember the location of the databag and the databag/item.json naming hierarchy.

Chef Vault

Of course we wouldn’t want our precious license keys (or passwords) out in the open like in the Data Bag example 3. The easiest way to avoid this is using Chef Vault. It automatically creates encrypted databags and you can assign privileges on which node may access which data bag.

As our Test Kitchen instances are not bootstrapped against a server and we don’t want to work with real data in our tests anyway, using Chef Vault here is no option. Outside of testing, accessing Vault is ok but in testing we should never interface with productions systems, right?

licenses = chef_vault_item('licenses', 'microsoft')

sql_server 'Install SQL Server' do
  action :install
  edition node['sql_server']['edition']
  license licenses['sql_server']
end

Luckily, the developers of chef_vault have implemented a fallback so we can use our Data Bag knowledge here:

suites:
 - name: standard
   provisioner:
     run_list:
       - recipe[sql_server::configure]
     data_bag_path: test/integration/data_bags/
     attributes:
       chef_vault:
         data_bag_fallback: true

We didn’t have to change our databag, only tell the helpers that a fallback is to be used. On a Chef Infra Server, we would still access the central data in the same way. It’s only the Kitchen configuration which makes the switch.

Don’t use Chef Vault in production

Chef Vault does not scale well, as it encrypts all secrets with a secret key per node. That leads to (#nodes) * (#secrets) different values and it’s easy to see that this won’t work beyond a handful of nodes.

For production setups, Hashicorp Vault is the recommended tool and is well-integrated with the Chef ecosysem.


  1. Curious about why this is under provisioner? It’s the “new” style from 2013. I wrote a blog on Kitchen style [return]
  2. Not sure about the default path? Definition in Chef base provisioner -> Lookup method -> Default test path [return]
  3. It is worth mentioning that you can also use encrypted databags. The procedure for this is a bit more complicated and there are other blog entries like from atomic-penguin on setting it up. Test Kitchen has an integration for this, but be careful about accidentally committing secrets to GIT. [return]