Three ways to chain events in Chef
Do X, then do Y. Doing things sequentially is common in configuration management. You have to install a package before you run a service. You have to write a configuration file before you start a server. Chef provides a lot of flexibility when it comes to sequencing events. Here are three ways you can get the right things to happen in the right order.
Write it in the order you want it to run
Chef executes resources in the order they appear in a recipe. Coming to Chef from Puppet, I found this to be a welcome surprise. Need to install the NTP daemon and then make sure the service is started? No problem. Just put your service resource after your package resource.
package 'ntp' do
action :install
end
service 'ntp' do
action :start
end
This is the most common way to sequence events. You can tell what the control flow is just by reading a cookbook from top to bottom.
Notify resources about changes
Chef provides a notification mechanism for signaling when things change. This lets you do things like restart a service when a configuration file changes. Want to lay down your own NTP configuration file and bounce the service when it’s updated? Easy. Have your template resource notify your service resource of the change.
template '/etc/ntp.conf' do
notifies :restart, 'service[ntp]'
end
service 'ntp' do
action :start
end
Something that surprised me when I first started working with Chef, was that notifications on resources queue, then trigger at the end of the run. So the recipe above takes the following actions:
- Updates the template.
- Queues a restart of the service.
- Starts the service.
- Restarts the service.
Sometimes you want a notification to trigger right after something changes,
instead of waiting until the end of the run. In that case, you can use the
:immediately
flag.
template '/etc/ntp.conf' do
notifies :restart, 'service[ntp]', :immediately
end
service 'ntp' do
action :start
end
Check if resources have changed
Chef resources are first class objects, which means you can ask them about
their state during the run. Every resource has an updated_by_last_action?
method which returns true
if the resource changed. Combining this with
sequential execution lets you build robust cookbooks.
Suppose you want to download and unpack a tarball. If the folder you’re
unpacking into exists, you’ll want to remove it first. But if the tarball
hasn’t changed, you’ll want to skip that step. Download the package with
a remote file resource resource, cache the result, and use the only_if
modifier on a directory resource to skip the removal if the tarball’s
unchanged.
tarball = remote_file '~/node-v0.8.20.tar.gz' do
source 'http://nodejs.org/dist/v0.8.20/node-v0.8.20.tar.gz'
end
folder = directory '~/node-v0.8.20' do
action :remove
only_if { tarball.updated_by_last_action? }
end
This lets you keep the linear flow of your cookbooks, while letting future resource take action based on what’s happened in the past.