I have a Chef cookbook, with multiple recipes, that installs a service. The way chef-client works, it re-converges every 15 minutes (or some other regular interval). Right now the first step of my cookbook is to stop the service, so the service will stop every 15 minutes, but I really want to avoid this. The problem is that the service needs to be stopped for some steps to be executed.
If I only had one or two conditions in my recipes' resources, I could simply do:
condition1 = ... # some true/false values
condition2 = ... #
service myservice do
only_if { condition1 or condition2 }
action :stop
end
resource1 do
only_if { condition1 }
end
resource2 do
only_if { condition2 }
end
But as I have a dozen or so conditions, in multiple recipes, it gets inelegant. Is there a way to do what this pseudocode does?
service_resource = service myservice do
# somehow don't execute this right now, but wait for
# signal from resource1 or resource2
action :stop
end
resource1 do
make sure service_resource executed first
only_if { condition1 }
end
resource2 do
make sure service_resource executed first
only_if { condition2 }
end
If there was some "notifies :before" mechanism in Chef (instead of e.g. "notifies :immediately"), I could use this, but I haven't found anything of the sort. Any ideas?
EDIT:
After thinking about this some more, another way of doing it, still not perfect but better, would be by taking advantage of the compile phase of Chef by defining a single attribute that's overwritten. That way at least we don't have to keep all conditions in one place.
# in an "attributes" file:
default["stop_service"] = false
# in some recipe:
service myservice do
action :stop
only_if { node["stop_service"] }
end
# ... later, or in some other recipe file
condition1 = ... # true/false
if condition1
# I think this executes before the only_if of the "service" resource
override["stop_service"] = true
end
resource1 do
only_if { condition1 }
end
What I would do:
Stage the configuration in a "staging" place, if this change, notifies stop, copy files, start the service.
Something along the line:
service "my_service" do
action :nothing
end
template "/staging/conf1" do
[... usual attributes ...]
notifies :stop,"service[my_service]",:immediately
end
template "/staging/conf2" do
[... usual attributes ...]
notifies :stop,"service[my_service]",:immediately
end
remote_file "/opt/my_service/etc/conf1" do
source "/staging/conf1"
notifies :start,"service[my_service]" # delayed here to allow all conf files to be updated.
end
remote_file "/opt/my_service/etc/conf2" do
source "/staging/conf2"
notifies :start,"service[my_service]" # delayed here to allow all conf files to be updated.
end
My personnal taste goes to loop over files/template definition hash for this kind of option with the same resources like:
node['my_namespace']['conf_files']['conf1'] = "template1"
node['my_namespace']['conf_files']['conf2'] = "template2"
and then looping over with
node['my_namespace']['conf_files'].each do |cfgfile,tmplname|
template "/staging/#{cfgfile}" do
source tmplname
notifies :stop,"service[my_service]",:immediately
end
remote_file "/opt/my_service/etc/#{cfgfile}" do
source "/staging/#{cfgfile}"
notifies :start,"service[my_service]"
end
end
For more complex use cases you may use a hash instead of a single value too
If there's more than 2 resources involved (executing specific commands for the conf file to validate it before replacing the actual one, etc.) a definition could fit.
Another could be to do all the staging work in the load_current_resource
of a LWRP but I may be going too far for the described use case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With