Easier way to apply one condition to a set of Chef resources

chefruby

In this part of a Chef recipe, there are 4 execute resources. They should only run when a file does not exists. This is added as a condition to the first resource, which then triggers the entire chain to be run.

# Set up default SSL cert
execute "defaultcert1" do
    not_if {File.exist?("/vol/webserver/cert")}
    command "mkdir /vol/webserver/cert"
    notifies :run, "execute[defaultcert2]", :immediately
end
execute "defaultcert2" do
    action :nothing
    command "ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem /vol/webserver/cert/server.crt"
    notifies :run, "execute[defaultcert3]", :immediately
end
execute "defaultcert3" do
    action :nothing
    command "ln -s /etc/ssl/private/ssl-cert-snakeoil.key /vol/webserver/cert/server.key"
    notifies :run, "execute[defaultcert4]", :immediately
end
execute "defaultcert4" do
    action :nothing
    command "chown -R ubuntu:ubuntu /vol/webserver/cert"
end

As you can see, it's a rather large bit of code to guard just 4 commands. Is there a better way to do this?

Best Answer

For your particular example, I'd suggest using Chef's directory and link resources, in preference to shelling out:

directory "/vol/webserver/cert" do
    user "ubuntu"
    group "ubuntu"
end

link "/vol/webserver/cert/server.crt" do
    to "/etc/ssl/certs/ssl-cert-snakeoil.pem"
end
link "/vol/webserver/cert/server.key" do
    to "/etc/ssl/certs/ssl-cert-snakeoil.key"
end

You don't need to guard these with not_if's and notifies - if the directory and links already exist with the correct attributes, Chef won't do anything.

On the other hand, say you want to use the snakeoil cert and key if nothing's there to begin with, but do nothing if the files already exist. In that case, I'd use not_if on the link resources:

directory "/vol/webserver/cert" do
    user "ubuntu"
    group "ubuntu"
end

link "/vol/webserver/cert/server.crt" do
    to "/etc/ssl/certs/ssl-cert-snakeoil.pem"
    not_if "test -e /vol/webserver/cert/server.crt"
end

link "/vol/webserver/cert/server.key" do
    to "/etc/ssl/certs/ssl-cert-snakeoil.key"
    not_if "test -e /vol/webserver/cert/server.key"
end

Compared to a string of execute resources, this communicates intent more clearly - it says that a directory needs to exist (with the correct user and group), and that a couple of symlinks should be created unless something is already present.

Finally, if you did have several related shell commands you needed to execute together, consider using the script resource.