Chef Libraries or Definitions

Chef libraries or definitions?

So, this is "primarily opinion based", but I'll answer it anyway. There are 4 distinct choices here:

  1. Definition
  2. LWRP
  3. HWRP
  4. "Library"

A definition is just a wrapper around one or more resources with some parameterization. However, definitions are not added to the resource collection. Meaning you can't "notify" or trigger events on a definition. They are solely for wrapping and naming a series of repeatable steps found in a recipe.

An LWRP (Light-weight resource and provider) is a Chef-specific DSL that actually compiles into an HWRP (Heavy-weight resource and provider) at runtime. Both LWRPs and HWRPs are Chef extensions. In addition to wrapping a series of repeatable tasks, *WRPs will create a top-level resource in Chef (like template or package) that's available for use in your recipe and other cookbook's recipes as well.

The difference between and LWRP and HWRP is really the Ruby. HWRPs use full-blown Ruby classes. If you aren't a Ruby developer, they may be a bit intimidating. Nonetheless, you should give it a try before writing and LWRP. LWRPs use a Chef-specific DSL for creating resources. At the end of the day, they compile to (roughly) the same code as the Heavy-weight counterpart. I'll link some references at the end. You have access to Chef resources inside either implementation, as well as the run_context.

Finally, "libraries" (notice the quotes) are often misunderstood and abused. They are Ruby code, evaluated as Ruby, so they can do pretty much anything. HWRPs are actually a form of a library. Sometimes people use libraries as "helpers". They will create a helper module with methods like best_ip_for or aggregate_some_data and then "mix" (Rubyism) that library into their recipes or resources to DRY things up. Other times, libraries can be use to "hack" Chef itself. The partial-search cookbook is a good example of this. Facebook talked about how they limited the number of attributes sent back to the server last year at ChefConf too. Libraries are really an undefined territory because they are the keys to the kingdom.

So, while I haven't actually answered your question (because it's opinion-based), I hope I've given you enough information about the best direction moving forward. Keep in mind that every infrastructure is a special snowflake and there is no right answer; there's only a best answer. I'd suggest sharing this information with your team and weighing the pros and cons of each approach. You can also try the Chef mailing list, on which people will give you many opinions.

Resources:

  • LWRPs
  • HWRPs
  • Libraries

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

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.

Does it make senses to test a call to a chef definition with chefspec?

You cannot test definition calls with ChefSpec. As definitions do not actually exist in the resource collection, you can't assert they have been called. You could assert than an object contained in the definition is called.

Sources:

  • I wrote it

Chef - share libraries between cookbooks

It's possible by using Chef Libraries.

make sure the functions are defined in your namespace via ruby modules:

module Foo
def very_useful_check
true
end
end

class Chef::Recipe::namespace
include Foo
end

Then you can use it in any recipe like:

myvar = Foo.very_useful_check


Related Topics



Leave a reply



Submit