Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With Chef, how to execute "resource 1" before "resource 2", conditional on "resource 2" executing?

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
like image 479
fsdj Avatar asked Oct 19 '22 15:10

fsdj


1 Answers

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.

like image 56
Tensibai Avatar answered Oct 22 '22 21:10

Tensibai