How to override a some Chef package resource properties, without needing to define ALL the other properties

chef

i use Chef to install RHEL 5.x packages in my nodes. I of course use the package resource, like so

package 'jdk'

As you can imagine, I have a LOT of package resource calls in a LOT of cookbooks.

Per the Chef package documentation, the resource has the retries and retry_delay properties, with default values of 0 and 2secs respectively.

Now I want to change this behavior so retries in 12 and retry_delay is 5 secs. Now I know I can do this

package 'jdk' do
  retries 12
  retry_delay 5
end

But then I'd have to do this in ALL package resource calls. Like I said I have a LOT of them, so I don't necessarily want to do this.

I have started writing a custom_resource, and call it say, mycompany_package can can do this, in my mycompany_package/resource/default.rb file.

# Package name
property :name, String, required: true, name_property: true
# Retries
property :retries, Integer, required: false, default: 12
# Delay between retries
property :retry_delay, Integer, required: false, default: 5

action :install do
  package name do
    action :install
    retries retries
    retry_delay retry_delay
  end
end

action :remove do
  package name do
    action :remove
  end
end

action :update do
  package name do
    action :update
    retries retries
    retry_delay retry_delay
  end
end

Now I can do this

mycompany_package 'jdk'

and I will get 12 retries and 5 retry_delays. Fair enough.

HOWEVER, this does NOT work

mycompany_package 'jdk' do
  flush_cache [:before, :after]
end

since mycompany_package does NOT define the flush_cache property. Now I have to define ALL of the package resource's properties in my mycompany_package cookbook. Yikes!

So what is the best way to override the retries and retry_delay default values of the package resource?

Best Answer

LWRP is probably overkill for what you're trying to do. Something to remember is that recipes are just Ruby and any Ruby you write outside of a block is executed during the compile phase. So with that in mind you can programmatically generate blocks that are executed in the converge phase.

The simplest example to solve your problem be to do something like this:

%w(jdk package2 package3).each do |pkg|
  package pkg do
    retries 12
    retry_delay 5
  end
end

To go a step further, you can use a Hash instead of an Array:

{
  'jdk' => {
    'version' => '1.8.0_92',
    'flush_cache' => [:before, :after]
  },
  'package2' => {
    'version' => '1.0.0'
  },
  'package3' => {}
}.each do |pkg,opts|
  package pkg do
    retries 12
    retry_delay 5
    version opts['version'] if opts.has_key? 'version'
    flush_cache opts['flush_cache'] if opts.has_key? 'flush_cache'
  end
end

You can also store the Hash or Array in attributes, but keep in mind that attributes aren't a true Hash, they're a Mash and there's some got'chas in there when trying to override attributes, but that's an entirely different discussion.

Related Topic