I'm trying to set up a workflow to develop Chef cookbooks locally. We're currently using Chef Server with the provisioned nodes using chef-client.
As part of the new workflow, we want to be able to start using Vagrant to test cookbooks locally to avoid incurring in the costs of testing on a remote machine in a cloud.
I'm able to launch and provision a local Vagrant machine, but the one thing I'm not really sure how to do is to have Chef load the local version of the cookbook, but still talk to the Chef server for everything else (environments, roles, data bags, etc.), so I don't have to upload the cookbook via knife every time I make a change I want to test. Is this possible?
In other words, can I make chef-client talk to the local chef-zero server only for the cookbooks but to the remote Chef server for everything else? Or maybe a different approach that would yield the same effect? I'm open to suggestions.
UPDATE
I think an example will help to express what I'm looking for. I'm realizing that this may not really be what I need, but I'm curious about how to achieve it anyway. In this scenario, a recipe reads from a databag stored in the remote Chef server
metadata.rb
name 'proxy-cookbook'
version '0.0.0'
.kitchen.yml
---
driver:
name: vagrant
provisioner:
name: chef_zero
platforms:
- name: ubuntu-12.04
suites:
- name: default
run_list:
- recipe[proxy-cookbook::default]
attributes:
recipes/default.rb
...
key = data_bag_item("key", "main")
....
Now, I know I can create something along the lines of:
data_bags/main.json
{
"id": "main",
"key": "s3cr3tk3y"
}
And have my kitchen tests read from that data bag; but that is exactly what I'm trying to avoid. Is it possible to either:
- Instruct test-kitchen to get the actual data bag from chef server,
- Have chef-zero retrieve a temporary copy of the data bags for local tests, or
- Quickly "dump" the contents of a remote Chef server locally?
I hope that makes sense. I can add some context if necessary.
Test kitchen is the best way to drive vagrant. It provides the integration you're looking for with chef zero. Enables you to completely emulate your production chef setup locally and test your cookbook against multiple platforms.
Test kitchen has replaced the older workflows I used to have chef development. Very well worthwhile learning.
Example
Generate a demo cookbook that installs java using the community cookbook. Tools like Berkshelf (to manage cookbook dependencies) and chef zero are setup automatically.
chef generate cookbook demo
Creates the following files:
└── demo
├── .kitchen.yml
├── Berksfile
├── metadata.rb
├── recipes
│ └── default.rb
└── test
└── integration
├── default
│ └── serverspec
│ └── default_spec.rb
.kitchen.yml
Update the platform versions. Kitchen is told to use vagrant and chef zero.
---
driver:
name: vagrant
provisioner:
name: chef_zero
platforms:
- name: ubuntu-14.04
- name: centos-6.6
suites:
- name: default
run_list:
- recipe[demo::default]
attributes:
Berksfile
This file controls how cookbook dependencies are managed. The special "metadata" setting tells Berkshelf to refer to the cookbook metadata file.
source 'https://supermarket.chef.io'
metadata
metadata.rb
Add the "apt" and "java" cookbooks as a dependencies:
name 'demo'
..
..
depends "apt"
depends "java"
recipes/default.rb
include_recipe "apt"
include_recipe "java"
test/integration/default/serverspec/default_spec.rb
Test for the installation of the JDK package
require 'spec_helper'
describe package("openjdk-6-jdk") do
it { should be_installed }
end
Running the example
$ kitchen verify default-ubuntu-1404
-----> Starting Kitchen (v1.4.0)
..
..
Package "openjdk-6-jdk"
should be installed
Finished in 0.1007 seconds (files took 0.268 seconds to load)
1 example, 0 failures
Finished verifying <default-ubuntu-1404> (0m13.73s).
-----> Kitchen is finished. (0m14.20s)
Update
The following example demonstrates using test kitchen with roles (works for data bags and other items you want loaded into chef-zero):
- Can the java cookbook be used to install a local copy of oracle java?
I think I found what I was looking for.
You can use knife to download the Chef server objects that you need. You can bootstrap this in .kitchen.yml so you don't have to do it manually every time.
.kitchen.yml
...
driver:
name: vagrant
pre_create_command: 'mkdir -p chef-server; knife download /data_bags /roles /environments --chef-repo-path chef-server/'
...
provisioner:
name: chef_zero
data_bags_path: chef-server/data_bags
roles_path: chef-server/roles
environments_path: chef-server/environments
client_rb:
environment: development
...
And then I just added the chef-server directory to .gitignore
.gitignore
chef-server/
There might be a less redundant way of doing this, but this works for me right now, and since I just wanted to document this, I'm leaving it like that.