Setting up a work environment
As we saw earlier, the Chef ecosystem comprises of three components: chef-server, chef-client, and a developer workstation.
We'll be developing all our beautiful Chef codes on our workstation. As we are developing a code, it's good practice to keep our code in some version control system such as git/svn/mercurial
and so on. We'll choose Git for our purpose and I'll presume you've a repository called chef-repo that is being tracked by Git.
The following software should be installed on your machine before you try to set up your workstation:
- Ruby (Preferably, 1.9.x).
- We need Chef and Knife installed on our workstation and it's pretty easy to go about installing Chef along with Knife using the Ruby gems. Just open up a terminal and issue the command:
- Once Chef is installed, create a
.chef
folder in your home directory and create a knife.rb
file in it.
Knife is a tool using which we'll use to communicate with a chef-server. Knife can be used for lots of purposes such as managing cookbooks, nodes, API clients, roles, environments, and so on. Knife also comes with plugins that allow it to be used for various other useful purposes. We'll learn more about them in later chapters.
Knife needs the knife.rb
file present in the $HOME/.chef
folder. The following is a sample knife.rb
file:
Connect to your chef-server web interface and visit the client section and create a new client with a name of your choice (ensure that no client with the same name exists on the chef-server):
Once you've created the client, a chef-server will respond with a public/private key pair as shown in the following screenshot:
Copy the contents of the private key and store them in ~/.chef/<NAME_OF_YOUR_CHOICE>.pem
Also, copy the private key for the chef-validator (/etc/chef/validation.pem
) from the chef-server to ~/.chef/validation.pem
.
Specify NAME_OF_YOUR_CHOICE
as the node name.
As you can see, we've specified cookbook_path
to be ~/code/chef-repo/cookbooks
. I'm presuming that you'll be storing your Chef cookbooks inside this folder.
Create the following directory structure inside ~/code/chef-repo
:
The cookbooks
directory will hold our cookbooks, the data_bags
directory will contain data bags, the environments directory will contain configuration files for different environments, and the roles directory will contain files associated with different roles.
Once you've created these directories, commit them to your Git repository.
Now, let's try to see if we are able to make use of the Knife executable and query the Chef server:
This command will list all the available API clients registered with the chef-server. As you can see, chef-eg01
is a newly created client and it's now registered with the chef-server.
Knife caches the checksum of Ruby and ERB files when performing a cookbook syntax check with knife cookbook test
or knife cookbook upload
. The cache_type
variable defines which type of cache to make use of. The most used type is BasicFile
and it's probably best to leave it at that.
The cache_options
is a hash for options related to caching. For BasicFile
, :path
should be the location on the filesystem where Knife has write access.
If you want the Knife cookbook to create a command to prefill values for copyright and e-mail in comments, you can also specify the following options in your knife.rb
file:
With this setup, now we are ready to start creating new cookbooks, roles, and environments, and manage them along with nodes and clients using Knife from our workstation.
Before we jump into cookbook creation and other exciting stuff, we need to ensure that we follow a test-driven approach to our Chef development. We will make use of test-kitchen to help us write Chef cookbooks that are tested thoroughly before being pushed to a chef-server.
test-kitchen can be installed as a gem:
Also, download Vagrant from http://www.vagrantup.com and install it.
If you want some help, use the help
option of the kitchen
command:
Now, let's create a new cookbook called passenger-nginx
:
Now, we'll add test-kitchen to our project using the init
subcommand:
The kitchen init
command has created a configuration file called .kitchen.yml
, along with a test/integration/default
directory.
It also went on to install a gem called kitchen-vagrant. kitchen needs a virtual machine to test run the chef code, and drivers are responsible for managing virtual machines. By default, kitchen makes use of Vagrant to manage the virtual machine.
Let's see what we have in our configuration file, kitchen.yml
:
The file is divided into four sections:
- Driver: This is where we set up basic stuff such as the SSH username and credentials. Under this section, we've a
name
property with a vagrant
value. This tells kitchen to make use of the kitchen-vagrant driver. - Provisioner: This tells kitchen to make use of a chef-solo to apply the cookbook to a newly created virtual machine.
- Platforms: This lists the operating systems on which we want to run our code.
- Suites: Here we describe what we wish to test.
Now, let's see what we have on our hands:
As you can see, it's listing two instances: default-ubuntu-1204
and default-centos-64
. These names are a combination of the suite name and the platform name.
Now, let's spin up one instance to see what happens:
So, this leads to the downloading of a virtual machine image for Ubuntu 12.04 and, eventually, the machine boots up. The default username for SSH connection is vagrant
.
Let us check the status of our instance again:
So, our Ubuntu instance is up and running. Now, let's add some meat to our recipe:
So, now we've got our recipe ready, let's let test-kitchen run it in our instance now:
So, here is what happened under the hood when kitchen converge
was executed:
- Chef was installed on an Ubuntu instance
- Our
cb-test1
cookbook and a chef-solo configuration were uploaded to an Ubuntu instance. - The Chef run was initiated using
run_list
and attributes defined in .kitchen.yml
If the exit code of the kitchen
command is 0
, then the command run was successful. If it's not 0
, then any part of the operation associated with the command was not successful.
Let's check the status of our instance once more:
So, our instance is converged, but we still don't know if nginx
was installed successfully or not. One way to check this is to log in to the instance using the following command:
Once you've logged in to the system, you can now go ahead and check for the presence of the binary named nginx
:
So, Nginx is indeed installed.
However, with kitchen, we no longer need to take the pain of logging in to the system and verifying the installation. We can do this by writing a test case.
We'll make use of bash automated testing system (bats), called for this purpose.
Create a directory using the following command:
Create a new file package test.bats
under the bats
directory:
Now, let's run our test using kitchen verify
:
So, we see that our test has successfully passed verification, and we can proudly go ahead and upload our cookbook to the chef-server and trigger a chef-client run on the concerned instance.