-
Notifications
You must be signed in to change notification settings - Fork 17
How to use toft gem and cucumber to test chef cookbooks
First you need a linux machine as a development environment because toft use lxc. Here we use vagrant to create a ubuntu natty virtual machine(other linux distribution should work too, but so far only ubuntu natty is fully tested). Follow the instructions to setup one.
Then add gem "toft", "~> 0.0.11"
to your Gemfile
if you are using bundler, or install gem directly:
gem install toft
Note that if you are using virtual machine like me then the development process will probably be writing code on your Mac and then run your cucumber features in your vagrant ubuntu box. This is actually not so retarding as it seems to be since vagrant’s NFS share folder works pretty well in this situation. Suppose your code dir structure is like this:
├── features ├── cookbooks ├── roles ├── Gemfile └── Vagrantfile
You can add this to your Vagrantfile
to share this dir between your Mac and virtual machine(see here to know more):
config.vm.network "33.33.33.10"
config.vm.share_folder("v-root1", "/home/vagrant/code", ".", :nfs => true)
This will map your current dir to /home/vagrant/code
in your virtual machine, and to run cucumber features in your virtual machine you have to run bundle install
in it.
Check the toft features to see how to use toft with cucumber. Here I will explain the code a bit.
Here is an example of env.rb
:
require 'rspec/expectations' require 'toft' CHEF_FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/chef' PUPPET_FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/puppet' Toft.cookbook_path = CHEF_FIXTURE_PATH + '/cookbooks' Toft.role_path = CHEF_FIXTURE_PATH + '/roles' Toft.data_bag_path = CHEF_FIXTURE_PATH + '/data_bags' Toft.manifest_path = PUPPET_FIXTURE_PATH World(Toft) include Toft # currently support "natty", "lucid" and "centos-6" n1 = create_node "n1", {:ip => "192.168.20.2", :type => "centos-6"} # default type is natty Before do @n1 = n1 end at_exit do n1.destroy end
Toft provides helper methods in the Toft
module so you have to include them in your world with World(Toft)
, and in the example we create a ubuntu natty node called “n1” to be shared by all features. Note that your cookbook_path and role_path can be configured so you can change the location of cookbooks and roles to be tested.
Note that if you are in a 64bit environment, create_node
will create 64bit lxc containers, and create 32bit lxc containers in 32bit environments.
If you receive the “Remote machine not responding” error on starting your container, there is an easy fix
[12] pry(main)> n1.start
RuntimeError: Remote machine not responding.
from /usr/lib/ruby/gems/1.8/gems/toft-0.0.6/lib/toft/node.rb:170:in `wait_remote_host_reachable'
Simply run lxc-prepare-host
and try again. This command will restart your local bind9 and dhcpd servers with the proper configuration
Toft provides helpers to manage nodes, run scripts and check environment status, for example:
Create a node with static ip
n1 = create_node "n1", {:ip => "192.168.20.2", :type => "centos-6"}
The default node type is “natty”, possible values are “lucid”, “natty” and “centos-6”.
Create a node using dhcp
n1 = create_node "n1", :type => "centos-6"
Get IP of the node
n1.ip # => ip of n1, useful when you use dhcp
Find a node
find("n1") # should be same as n1
Find all nodes
find(:all) # an array containing all existing nodes
Start and stop node
n1.start
n1.running? # => true
n1.stop
n1.running? # => false
Destroy node
n1.destroy
Add and remove cname to nodes
n1.add_cname "foo"
n1.remove_cname "foo"
Remove files and dirs
n1.rm "/tmp/*"
Run chef recipes with or without overriding attributes
n1.run_chef "recipe[test::one]" # run a single rcipe
Run multiple recipes or roles
n1.run_chef ["recipe[test::one]", "recipe[test::two]", "role[some_role]"] #run multiple
Override attributes
n1.run_chef "recipe[test::one]", Toft::ChefAttributes.new(table) # run a chef recipe with override attributes
# here table is a cucumber table like:
|key|value|
|one|one|
|two.one|two_one|
|two.two|two_two|
|three|three|
Run puppet manifests
n1.run_puppet "manifests/test_module.pp"
Run puppet manifests with customized config files
n1.run_puppet(run_list, :conf_file => "puppet_modules.conf")
Run ssh commands
n1.run_ssh "netstat -nr" # run a shell command through ssh
You can also access the ssh command output by passing a block to run_ssh
result = n1.run_ssh "pwd" do |output|
output.should include("some_dir") # the output here is guaranteed to be the whole output of the command execution
end
# result.stdout and result.stderr contains complete output
If there is the ssh command exit with a non-zero status, CommandExecutionError will be raised, you can catch it and access the stdout and stderr:
begin
r = find(node).run_ssh(cmd)
rescue CommandExecutionError => e
e.stdout.should include(s)
end
n1 = find("n1") n1.file("/tmp").exist? # true n1.file("/tmp").mode # 1777 n1.file("/tmp").owner # root n1.file("/tmp").group # root n1.file("/tmp").filetype # directory
Again, the toft source code is tested used cucumber, so by checking feature you should see all tests against toft api and usages of its features.