In my last post, MVT: Foodcritic and Travis CI I described the process for having Travis CI look after your cookbooks and run Foodcritic, the cookbook lint tool, on your cookbook after each git push
. In this post, we'll iterate on the "Minimum Viable Test" idea by adding in support for knife's cookbook testing.
Wait, I'm already running foodcritic, do I really need to run knife cookbook test
, too?
I'll use a very simple example to demonstrate that you do.
Let's create a very basic cookbook:
knife cookbook create very_basic
** Creating cookbook very_basic
** Creating README for cookbook: very_basic
** Creating metadata for cookbook: very_basic
Next, we'll write a flawed recipe:
``` ruby cookbooks/very_basic/recipes/default.rb package "flawed" do action :nothing end end
Now, run foodcritic on this cookbook:
``` sh
foodcritic cookbooks/very_basic
Foodcritic doesn't throw any errors or find any problem with the cookbook.
Let's try testing it with knife:
knife cookbook test very_basic
checking very_basic
Running syntax check on very_basic
Validating ruby files
FATAL: Cookbook file recipes/default.rb has a ruby syntax error:
FATAL: /Users/nharvey/projects/chef-hosted/.chef/../cookbooks/very_basic/recipes/default.rb:22: syntax error, unexpected keyword_end, expecting $end
OK, it should now be obvious that knife cookbook test
should be included as part of our MVT.
To get Travis CI running knife cookbook test
for us, we'll need to add or update the following files:
Of course, this assumes you've configured your cookbook as described in the previous post. Let's start with the Rakefile.
``` ruby Rakefile
desc "Runs knife cookbook test" task :knife do Rake::Task[:prepare_sandbox].execute
sh "bundle exec knife cookbook test cookbook -c test/.chef/knife.rb -o #{sandbox_path}/../" end
task :prepare_sandbox do files = %w{*.md *.rb attributes definitions files libraries providers recipes resources templates}
rm_rf sandbox_path mkdir_p sandbox_path cp_r Dir.glob("{#{files.join(',')}}"), sandbox_path end
private def sandbox_path File.join(File.dirname(FILE), %w(tmp cookbooks cookbook)) end
In the file snippet above, I've only included the parts that are relevant for getting knife working. I'll include the full source of the Rakefile at the end of the article.
Next, let's add this rake task to our .travis.yml file.
``` ruby .travis.yml
language: ruby
gemfile:
- test/support/Gemfile
rvm:
- 1.9.2
- 1.9.3
script:
- bundle exec rake knife
To successfully run the knife command, Travis CI will need a very minimal Chef configuration.
``` ruby test/.chef/knife.rb cache_type 'BasicFile' cache_options(:path => "#{ENV['HOME']}/.chef/checksums")
And, of course, we'll need to add Chef to our Gemfile. Be sure to specify a modern version as Travis CI will use 0.8.10 by default (at the time of this writing).
``` ruby test/support/Gemfile
source "https://rubygems.org"
gem 'rake'
gem 'chef', '~> 10.12.0'
That's it. On your next git push
Travis CI should run knife cookbook test
on your cookbook.
To run the rake tasks locally, you'll need to tell bundler where the Gemfile is, or you'll need to move it to the root directory of your cookbook and update .travis.yml appropriately. Use the following command to run your tests locally:
BUNDLE_GEMFILE=test/support/Gemfile rake knife
BUNDLE_GEMFILE=test/support/Gemfile rake foodcritic
You can checkout this Github compare view to see the changes made to the code from the previous post.
``` ruby test/.chef/knife.rb cache_type 'BasicFile' cache_options(:path => "#{ENV['HOME']}/.chef/checksums")
``` ruby .travis.yml
language: ruby
gemfile:
- test/support/Gemfile
rvm:
- 1.9.2
- 1.9.3
script:
- bundle exec rake knife
- bundle exec rake foodcritic
The Rakefile was refactored a bit since the previous post:
``` ruby Rakefile
task :default => 'foodcritic'
desc "Runs foodcritic linter" task :foodcritic do Rake::Task[:prepare_sandbox].execute
if Gem::Version.new("1.9.2") <= Gem::Version.new(RUBY_VERSION.dup) sh "foodcritic -f any #{sandbox_path}" else puts "WARN: foodcritic run is skipped as Ruby #{RUBY_VERSION} is < 1.9.2." end end
desc "Runs knife cookbook test" task :knife do Rake::Task[:prepare_sandbox].execute
sh "bundle exec knife cookbook test cookbook -c test/.chef/knife.rb -o #{sandbox_path}/../" end
task :prepare_sandbox do files = %w{*.md *.rb attributes definitions files libraries providers recipes resources templates}
rm_rf sandbox_path mkdir_p sandbox_path cp_r Dir.glob("{#{files.join(',')}}"), sandbox_path end
private def sandbox_path File.join(File.dirname(FILE), %w(tmp cookbooks cookbook)) end
``` ruby test/support/Gemfile
source "https://rubygems.org"
gem 'rake'
gem 'foodcritic'
gem 'chef', '~> 10.12.0'
A big "Thank You!" shout-out to Seth Vargo for writing most of the code used in this post!
Reposted from Nathen Harvey's blog