I am trying to better organize some Chef recipes by collecting common Ruby logic in a helper library. I've seen examples declaring a class in the library (i.e. class Chef::Recipe::MyHelper) with a few reusable methods inside. I've also seen examples using a module in a similar manner. In my case I wanted to use a resource inside a few of these methods.
For example, say I want to provide a helper method that takes an array of service names and loops through stopping each one using the service resource. I want to cleanup the recipe files as much as possible and keep some of that logic out by just calling a "stopServices(serviceList)" method.
If I define the helper library like:
class Chef::Recipe::MyHelper
def self.stopServices(serviceList)
serviceList.each do |svc|
service "#{svc}" do
action :stop
end
end
end
end
Then in my recipe I use:
MyHelper.stopServices(serviceList)
I get the error: "undefined method 'service' for Chef::Recipe::MyHelper:Class".
Is there an easy way to be able to use resources in a library like that? (Whether the library contains MyHelper as a class or module)? Is this just a bad practice that I'm violating? I've done a lot of searching and can't find anybody asking something similar which leads me to believe I'm probably doing something I shouldn't so any alternative suggestions would be much appreciated too.
Libraries are a way to abstract complex Ruby code away from a cookbook recipe.
To group resources (Chef DSL code) you should use either
service
resources that you can :start
, :stop
, :restart
, etc; or package
resources that you can :install
, :upgrade
, :remove
, etc).Update
A definition that would solve your example problem:
# cookbooks/common/definitions/common_stop_services.rb
define :common_stop_services, :services => [] do
params[:services].each do |svc|
service svc do
action :stop
end
end
end
then use it like this:
# my_cookbook/recipes/my_recipe.rb
common_stop_services "my_recipe_services" do
services [ 'svc1', 'svc2' ]
end
Obs: It's probably worth it asking yourself if you really want to wrap multiple service stoppage in a common method. Usually a service action is notified by some other resource (classic example is a configuration file change notifying the reconfigured service to restart, but other patterns apply as well).
Obs2: CamelCase is only used for Classes
and Modules
in Ruby code. I recommend reading the style guide.
Obs3: Even if you were to implement that code in a library, you probably don't want to use a Class
. You're never going to instantiate MyHelper
, so what you want is a Module
instead.