How can a chef LW resource attribute default value refer to another attribute?

3k views Asked by At

I'm trying to set the default value of one resource attribute to the value of another attribute.

I'm defining a resource with the following definitions in it for a tomcat cookbook I'm building. I want to have "name" and "service_name" attributes that can be set independently. When service name is not set, I want it to default to whatever is provided for "name."

The following does not work as I would expect:

attribute :name,         :kind_of => String, :required => true, :name_attribute => true
attribute :service_name, :kind_of => String, :default => :name

Notice the ":default => :name" at the end of the second line. When I refer to my resource in a new block in a recipe as such

my_tomcat "install tomcat" do
  name "foo_bar"
end

The attribute values get assigned as

 name = "foo_bar"
 service_name = "name"

which is not what I expected. I wanted service_name to be "foo_bar" unless it was explicitly set.

I've tried

attribute :service_name, :kind_of => String, :default => new_resource.name
attribute :service_name, :kind_of => String, :default => @new_resource.name

but those don't compile.

Is there a way to do what I'm trying to do?

2

There are 2 answers

5
sethvargo On

Since those are class-level methods, you need to use the lazy attribute:

attribute :service_name, kind_of: String, default: lazy { |r| r.name }

It's also worth noting that:

attribute :name, kind_of: String, required: true, name_attribute: true

is entirely redundant. That's the default...

0
Rich Mills On

I was unable to use Seth's "lazy" evaluation, but was able to simulate by creating a dynamic accessor method.

This other post was useful: How to implement a dynamic attribute default in chef LWRP definition

First, the definition in my resource definition file:

attribute :service_name,        :kind_of => String, default: nil

Next, the accessor block at the bottom of the same resource definition file:

def service_name( arg=nil )
  if arg.nil? and @service_name.nil?
    set_or_return( :service_name, @name, :kind_of => String)
  else
    set_or_return( :service_name, arg, :kind_of => String )
  end
end

Which effectively sets the value of "service_name" the first time it is used in my provider code.

This works for all combinations of

resource "title" do
  # name defaults to "title"
  # service_name defaults to "title"
end
resource "title" do
  name "my_name"
  # service_name defaults to "my_name"
end
resource "title" do
  name "my_name"
  service_name "my_service_name"
end

Thanks, again, for your help. I hope someone else finds this useful in the future.

Rich