Exception handling for CHEF data bags
On a recent cookbook, I found an interesting use case for conditional data_bags. In my use case, the client wants to give a license file to install the application. Since the license file is an XML document and not a key, this posed an interesting question on how do we handle this to cover the most use cases in the smallest amount of code. The plan became to support three solutions to cover all the different use cases.
- A node attribute to which a URL could be assigned to pull the license file
- A CHEF::DataBagItem that would contain a Base64 encoded version of the license file
- A node attribute to install in Evaluation mode
The first step was to add a resource parameter to our LWRP and have this TrueFalse Class object represent if we should install in evaluation mode. The next part was to add a remote_file object in the LWRP to pull the file if the attribute for the license_file is set (Default value is nil). The last step would be to create a file object to generate the file from the contents of the data_bag if that data_bag exists. I experimented with several models but decided to leverage this model. I moved this code into a method in the Provider and declare the output as a variable used by the main action.
# So this is a bit of a workaround to deal with checking to see if a data bag exists and # more importantly to see if the item exists. The BEGIN..RESCUE will prevent accidental # failures and return nil if there is a failure. We are using StandardError to capture any # error that occurs. We tested with exceptions on Net::Http::Exception but this failed to # respond correctly. Note, the data_bag will report an error to the console but be handled # correctly. # begin application_license_bag = data_bag_item('application', 'license') rescue StandardError Chef::Log.info('The DataBagItem(\'license\') was not found. Installing the package in Evaluation Mode.') return nil # Return nothing since the data_bag was not found or the item does not exist end file license_file do content Base64.decode64(application_license_bag['license']) sensitive true provider Chef::Provider::File action :create end
Now that I had my solutions in place, I could handle the different scenarios very easily. I am also able to add specific tests in ChefSpec by creating a single test for the four scenarios with using node.override calls and stub_data_bag_item. All we need to do now is document the solution and we can integrate conditional data_bag items into our LWRP and recipes. Technically, I could throw the File object into the begin..rescue block but for the sake of clarity and readability, I kept it out of the block. If the data_bag_item is not found, then we will return nil on this method and the rest of the code can work off of that trigger.