Notifications, configuring and restarting service


#1

Hello,

Let’s assume the following code that sums up my current recipe i.e. installation of a package and configuration of the service from this package.

#
# install_recipe.rb
#
package 'mypackagewithmyservice'

template 'configfile1'
  notifies :restart, 'service[myservice]', :immediately
end
...
template 'configfileN'
  notifies :restart, 'service[myservice]', :immediately
end

service 'myservice' do
  action       [:start, :enable]
end

execute "a command that waits for the service to be fully responsive"

#
# specific_configuration_recipe.rb
#
execute "a command that communicate with the server to do further configuration through e.g. REST API"

The problem with this code:

  • The service is restarted on every configuration change even during the first chef run when installing the package and configuring the service for the first time.
  • Changing the notifies last argument to :delayed seems to not be an option here. The problem is that we need to have a fully configured server before trying to run the second recipe (specific_configuration_recipe.rb)

I guess this pattern can be quite typical for more complex services and it seems similar questions have been asked without a definitive answer (see http://stackoverflow.com/questions/21066591/minimize-service-restarts-from-chef-notifications). My current solution to the above problems is as follows.

#
# install_recipe.rb
#
package 'mypackagewithmyservice'

config_changed_action = "config changed for myservice"
template 'configfile1'
  notifies :run, "ruby_block[#{config_changed_action}]", :immediately
end
...
template 'configfileN'
  notifies :run, "ruby_block[#{config_changed_action}]", :immediately
end

ruby_block config_changed_action do
  block { node.set[:myservice][:config_changed] = true }
  action :nothing
end

ruby_block "restart myservice" do
  block {}
  notifies :restart, "service[myservice]", :immediately
  only_if { node[:myservice][:config_changed]  }
end

service 'myservice' do
  action       [:start, :enable]
end

execute "a command that waits for the service to be fully responsive"

#
# specific_configuration_recipe.rb
#
execute "a command that communicate with the server to do further configuration through e.g. REST API"

But the question is if there is a more elegant way to do it? Maybe it is a good starting point for a future enhancement? I was thinking about adding additional argument to the notifies property like :delay_to_the_nearest_resource_block_below_this_line.

Regards,
Piotr


#2

You can set your execute to action :nothing and use multiple notifies so it only runs when the template is modified.

template 'configfile1'
  notifies :restart, 'service[myservice]'
  notifies :run, 'execute[your_thing]'
end

execute "a command that waits for the service to be fully responsive"
  action :nothing
end

#3

I didn’t think about this one but anyway it does not solve my second problem. Running for example chef-client -r "install_recipe, specific_configuration_recipe" will not work because the restart and the wait_for_the_service_to_become_fully_responsive resources will be deferred to the very end of a chef run, thus executing a command that communicate with the server to do further configuration through e.g. REST API will most probably fail. To give a better understanding of my problem I will add that my service boots ~3 minutes before being able to handle RESTful requests.


#4

Sounds like it’s the perfect use for a custom resource handling all templates, then this custom resource will notify the service restart (would work for any inner resource updated.

For the second part, just rely on multiples runs, your specific_configuration_recipe.rb could start with a ‘guard’ line, skipping it if the service is not responsive and will converge on second run of chef (delay between the runs being up to you).

Doing everything in one pass is not always the best path IMHO.

Doc for custom resources