How can I use a Chef resource within a library module? (Or should I...)?

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.

Answers


Libraries are a way to abstract complex Ruby code away from a cookbook recipe.

To group resources (Chef DSL code) you should use either

  • Definitions (simplest option) which you can use like a regular Chef resource in your recipes; or
  • LWRPs which are more complex but support different actions (think 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.


Need Your Help

caching images on UITableViewCells with AlamofireImages in my Swift app

ios swift uitableview alamofireimage

I'm writing a Swift app that displays photos fetched from server on each cell of my UITableViewController.

Angular 2 (RC.6) Directive @Input being converted to string

angular angular-directive

I'm having a weird issue with a boolean that I'm trying to pass to my directive as an Input. For some reason angular is converting the boolean to a string despite the fact I have it typed as a bool...