Professional Documents
Culture Documents
Environment Setup
1. Access Version Control Contact the [INSERT CONTACT] for questions on how to
get Git Access, which is where the Baxter cookbooks live.
2. Setup Dev Environment - Setup a virtual machine for developing and testing recipes
locally. Assumptions are that Chef developers will have either:
VirtualBox (what does Baxter currently use?) installed locally with a VM equivalent to the
target deployment environment
SSH to a virtual server in AWS (Jenkins job exists to create Chef dev environment in
AWS via a CloudFormation template; see the DevOps team for access)
Appropriate text editor (Notepad ++, Sublime Text, etc.) and ChefDK installed locally
without virtual machine, along with Test Kitchen for testing recipes in virtual environment
3. Configure Local Dev (if not using AWS) From the local environment, download from
Nexus and install:
Chef Development Kit for
i. Mac OSX
ii. Windows
iii. Ubuntu
iv. Debian
v. RHEL
vi. SUSE
Git client for communicating with the Version Control System. For Windows, TortoiseGit
or Git for Windows
A robust text editor such as Sublime Text 3 for authoring recipes.
4. Checkout Recipes From the local ecosystem, create a working directory for your
recipes then use the Git client to check them out from the appropriate repository.
5. Validate Dev Environment From the working directory, run chef verify to verify your
local environment is setup.
If you completed these 5 steps, congratulations! You now have a working Chef development
ecosystem and can begin developing and testing recipes.
Local Development
In order to run the example cookbook, the chef-client must be installed on your target system.
For ease of use and to avoid dependencies that may slow down Chef developers early on, we
are recommending the use of Chef-Zero in local environments.
Chef-Zero is a client-side application that does not require connectivity to a Chef Server; rather,
it spins up a very lightweight, in-memory Chef Server that your local cookbooks are then
transferred to. When the Chef-Client run starts, it pulls cookbooks from the in-memory Chef
Server to mimic the real client-server relationship.
Cookbooks must be locally available in order to run Chef-Zero, and it requires two files:
1. Client.rb the Chef-Client configuration file. This file specifies the location where Chef-
2. node.json the file containing instructions for target nodes (run list). Without this file,
Chef Zero does not know what cookbooks to run on itself. The location of the node.json
file is configurable with the j command line switch.
Below is an example node.json file specifying the dns cookbook followed by the
example_cookbook. Notice the syntax; dns::default specifies the default recipe of the
dns cookbook. Why is there no specific recipe selected for the example_cookbook?
The default behavior of a run list is to pull the default recipe if none is specified. In the
below example, both the dns and example_cookbooks are set to run their default
recipes. It was not necessary to specify the default recipe for the dns cookbook.
In order to execute the example_cookbook, ensure that it is present in the correct directory (in
this case, /opt/chef/chef-repo/cookbooks or c:\chef-repo) and then simply run:
The z flag is telling Chef-Client to run in Zero mode, which will spin up the in-memory Chef
For security reasons, Baxter has an internal RubyGems server with only those recipes approved
for use within Baxter . Once new recipes are working and tested locally, any new gems that
need to be added to the internal RubyGems server should be done before the new recipe(s) can
be deployed to non-local environments.
It is the responsibility of each Chef developer to identify any gems used by internal or
community cookbooks so that the required gems (and their dependencies) can be made
available on the Baxter Ruby Gems server. Please contact the DevOps for more information on
getting gems setup in the internal RubyGems server.
Cookbook Versioning
Cookbooks should always be versioned before changes are committed to Git as the Chef
Server will freeze all uploaded cookbooks by default (meaning they cannot be overwritten
without updating the version). Cookbook versions are maintained in each cookbooks
metadata.rb file in the root cookbook directory and should follow semantic versioning as follows:
MAJOR version when a breaking change is made
MINOR version when backwards-compatible functionality is added
PATCH version when backwards-compatible bug fixes are added
Examples:
1. apache 0.0.1 would be the initial commit of the apache cookbook; this cookbook is
incomplete
2. apache 0.1.0 would be the first working version of the apache cookbook; this cookbook
is not yet used in production but should run successfully
3. apache 1.0.0 would be the first working version of apache for production use
4. apache 1.1.0 would include a non-breaking update to the production apache cookbook
5. apache 1.1.1 would fix a minor bug in the apache 1.1.0 cookbook
This may result in unintended changes being made to nodes, especially if the cookbook being
updated is used by many nodes. Cookbook versions in a node run list or role run list can be
specified as follows:
{
run_list: [
recipe[javamsp::groups@1.0.0],
recipe[dns@1.1.0]
]
}
In the above example, the recipe groups within the javamsp cookbook is constrained to
version 1.0.0 of the javamsp cookbook with the
cookbook_name::recipe_name@cookbook_major.cookbook_minor.cookbookpatch syntax. The
recipe default within the dns cookbook is constrained to version 1.1.0 of the dns cookbook,
because a specific recipe is not defined for the dns cookbook, so the default is used.
Testing
All cookbooks should be thoroughly tested before being pushed to Git. All role changes should
be made only after all relevant tests have passed in pre-production regions. Any role changes
intended for production release will need to secure approval of a Baxter release manager before
the Jenkins job is triggered.
Cucumber tests should be written prior to writing a cookbook and should be written to fail. As
the cookbook is beginning to take shape, the Cucumber tests should start passing until all tests
desired to verify the state of the system are passing. If the Cucumber tests and knife cookbook
tests for a particular cookbook have all passed, the cookbook is likely ready to be committed to
Git.
This .feature file (in this case, ~/chef-repo/features/apache.feature) is backed up with a Ruby file
in (~/chef-repo/features/stepdefs/apache.rb) file containing the code necessary to test this
feature. In this case, the code would appear as follows:
The above example illustrates the Ruby code and regular expression necessary to verify that
the return values from the command which httpd correctly matched the feature files
expectation that the result equal /usr/sbin/httpd.
The following example illustrates a Cucumber testing verifying that the Apache service is
running by looking for the return value httpd (pid *random pid*) is running from the command
service httpd status:
This is because the Process ID (PID) of the httpd service is not known at runtime and is
dynamically generated, thus necessitating the use of a regular expression to match the
unknown process ID to an expected string.
Many common bash commands can utilize the same code to verify a systems state. The
Continuous Integration section of this document discusses the use of Cucumber in relation to
the Continuous Integration flow in more detail. More information about Cucumber testing can
be found here.
FoodCritic
FoodCritic is another Chef lint tool for verifying syntax and Chef best practices prior to node
convergence. FoodCritic is automatically run on every Git commit via a Jenkins job, but should
be used in conjunction with knife cookbook test locally prior to committing changes. FoodCritic
is a Ruby gem and requires Ruby 1.9.2 or greater.
Test Kitchen
Test Kitchen is a RubyGem that provides infrastructure developers an integrated testing
platform by leveraging virtual machines to provide pristine environments that are easily spun up,
tested upon, and blown away. Test Kitchen supports test environments in AWS, VirtualBox,
and OpenStack or using Vagrant locally. The Chef Development Kit also ships with Test
Kitchen for getting up-to-speed quickly.
Getting Started
1. Download VirtualBox and Vagrant. If vagrant is already installed on the system with
version lower than 1.6.5, kindly uninstall the lower version and reinstall v1.6.5 to avoid
any version conflict issues.
2. Download Git for Windows and during installation, make sure to check the Use Git and
optional Unix tools from the Windows Command Prompt checkbox, otherwise accept
the defaults.
3. Enable Virtualization in your System BIOS. Usually, this is accomplished by:
a. Restarting the computer
b. Press the ESC key during startup
c. Select System BIOS by pressing the F10 key
d. Select System Configuration or Advanced
e. Select Device Configurations
f. Select Virtualization Technology
Save and quit
4. Download the Chef Development kit for Windows
7. Clone the Chef repositories that are needed via TortoiseGit or Git CLI.
8. Open the ~/chef-repo/cookbooks/kitchen-docs/Berksfile and update the paths to point to
each cookbook on your local filesystem.
9. Open the command line and change directory to the kitchen-docs cookbook
10. Type kitchen create kitchen-centos-66
11. Once the creation is finished, type kitchen converge kitchen-centos-66
12. If the converge fails because the Omnibus installer cannot be found, open your
command line and run vagrant plugin install vagrant-proxyconf
Test Kitchen has used the VirtualBox platform and the Vagrant VM driver to quickly build a
CentOS 6.6 virtual machine using the Vagrantfile.erb embedded Ruby template in the kitchen-
docs root directory. This template is used so that the non-default http and https_proxy values
can be set when the Operating System starts, allowing the VM to have access to the internet
through the proxy using your AD credentials.
Once the Vagrantfile has been used to provision the VM, the VM downloads the Chef client from
the omnibus URL passed into Test Kitchen from the .kitchen.yml file. After Chef has been
installed, a custom client.rb file has been generated using default values from Test Kitchen
along with the added trusted_certs_dir specified in the Provisioner section of the .kitchen.yml
The .kitchen.yml file is used to describe how Test Kitchen should provision your virtual test
environment; it is also possible to hook Test Kitchen up with other service providers including
AWS, OpenStack, and Rackspace. The driver section of the file indicates which driver to use;
in our case, we simply used a local environment powered by VirtualBox and Vagrant.
You will also need to identify the appropriate AMI ID and create an SSH key pair that Test
Kitchen can use to connect to the instance and run the Chef suites. An example .kitchen.yml for
EC2 is below:
The provisioner section also includes overrides to the client.rb file (which is dynamically
generated by Test Kitchen on the test node in /tmp/kitchen/client.rb). The .kitchen.yml file in the
kitchen-docs cookbook includes an override for the trusted_certs_dir, which points to
/etc/chef/trusted_certs. This is used for communications with the Artifact Repository; the root
Baxter CA cert must exist in /etc/chef/trusted_certs on the target node if your recipes are using
remote_file for downloading artifacts. The root_crt.rb kitchen-docs recipe includes the code to
copy the root cert to this directory.
The platforms section of the file indicates which version of OS the tests should be run on, as
well as any specific configuration you would like to apply to the OS (including URL to find the
The suites section of the file indicates which recipes to execute on the target node. You can
also customize memory and CPU for the VM within the suites section. If you have written unit
tests for your cookbook, they will be executed when you run kitchen converge, kitchen verify, or
kitchen test.
At this point, you should be able to navigate to localhost:8080 on your workstations browser
and view a test page served by Apache. You should also be able to navigate to localhost:4567
and view the Test Kitchen documentation.
Additionally, integration and unit tests have been written for the kitchen-docs cookbook. These
tests can be viewed in the test and spec folders respectively; the spec folder contains
ChefSpec unit tests while the test folder contains ServerSpec and BATS integration tests. See
below for additional detail on each testing method, but for now you should just be able to run
kitchen verify kitchen-centos-66 to see the integration tests pass.
This allows you to seamlessly test interdependent cookbooks with Test Kitchen. Full Berkshelf
documentation is available here.
Below is a screenshot of the Berksfile located in the kitchen-docs cookbooks root directory:
The source https://supermarket.chef.io declaration at the top of the Berksfile indicates that
Berkshelf should reach out to the Chef Supermarket site to download dependent cookbooks if a
path is not otherwise specified. In our case, weve got two of the necessary cookbooks locally
(example_cookbook and kitchen-docs are custom cookbooks that are not available in the
community, but IIS is a community cookbook and can be retrieved from the Supermarket, hence
no path: specification).
In the above example, were going to reach out to the Supermarket to download version 4.0.0
of the IIS cookbook (this also illustrates locking cookbook dependencies to specific versions so
that conflicts dont emerge if a community cookbook is upgraded to a new major version).
Login to Vagrant VM
Once the vagrant boxes are created and converged, you can also login to the VM and verify any
configuration if required. Default vagrant configuration is to use vagrant user with
insecure_private_key key [default location is
%USERPROFILE%\.vagrant.d\insecure_private_key], or use root or vagrant users with
vagrant as password.
Default IP and port for vagrant VM are 127.0.0.1 and 2222 respectively. For further details on
vagrant configurations related to multiple VMs and ports, kindly refer the vagrant
documentation.
The easiest way to login to a Test Kitchen instance is to just run kitchen login [machine name].
In the kitchen-docs cookbook, that would be accomplished by running kitchen login default-
centos-66 after the VM has been created. This command will allow you to SSH into the test
Dependencies
ChefSpec requires the ChefDK in order to run correctly, and it also requires the Ruby DevKit.
The DevKit contains Ruby Development tools including C compilers and Make, which allows
you to build native Gem extensions locally. I extracted the Ruby DevKit to C:\DevKit and then
configured my C:\DevKit\config.yml to point to the Vagrant Ruby installation as follows:
You must have the Chef Development Kit installed and your $PATH variable must include the
following IN THIS ORDER:
1. C:\opscode\chefdk\bin
2. C:\opscode\chefdk\embedded\bin
Once these environment variables are set, you should continue the below instructions.
The kitchen-docs cookbook includes multiple example ChefSpec unit tests (note: these are
rudimentary tests and should not be used as a catch-all).
Heres an example of a ChefSpec test that verifies that your code will correctly create the
directory /root/.ssh and the file /root/.ssh/known_hosts:
Then, make sure you have the Bundler gem installed and navigate to the kitchen-docs directory
in your command line (terminal on Mac or Command Prompt on Windows) and run the
command rspec. This will execute all tests specified in the default_spec.rb file and post the
verbose outputs into STDOUT.
~/chef-repo/cookbooks/kitchen-docs/test/integration/kitchen/bats/some_test.bats
~/chef-repo/cookbooks/kitchen-
docs/test/integration/kitchen/serverspec/some_test_spec.rb
BATS
BATS is a method for testing the end state of target nodes by running bash commands and
expecting certain exit codes (generally 0, for success). Here is an example BATS test file:
The c.path variable is setting the environment PATH variable on the remote system so that
Linux commands can be executed to verify the state of a system and its components. An
example ServerSpec test file is below: