How do you avoid variable conflicts in custom resources?


I have a cookbook with a custom resource

resource_name :foobar
property :service_name, String, required: true
property :display_name, String, required: true

puts "#{service_name}"

I find that if I apply a role that includes the alert logic cookbook, the variable “service_name” conflicts.

Their cookbook has a method in a library helper with the name “service_name”

How can I isolate my custom resources from using methods in other cookbooks?

Chef 12.9.0
Windows 2012r2


Couple of things puzzle me, the code in the alertlogic cookbook is name spaced so should not conflict with anything else, however it is a module which is used as a mixin to Chef::Recipe (this means all of the methods are now available in any instance of Chef::Recipe). Your custom resource will be of type Chef::Resource.

Is your custom resource written in the old or new style DSL? Where does the code reside, is it in the resources folder or libraries?

Are you familiar with Pry and setting breakpoints? Rather than puts service_name (there is no need for string interpolation here) you can add a break point and view as much as you like of the resource and methods available.


I think using new_resource.service_name would disambiguate things.



Here is a link to the resource. It is a custom resource (the new chef 12.5 syntax, not a LWRP).

Thanks for the tip on new_resource. I’ll look into that. I want to understand why there is a conflict in the first place. Shouldn’t alert logics cookbook be in a different scope and isolated from my custom resource?



TL;DR; Easiest to change your attribute name

I couldn’t get your example to work but I can read and understand the code.

I’m guessing you are installing alert_logic on a Windows box? I’m assuming this because the default recipe of alert_logic looks like this

if platform_family?('windows')
  include_recipe 'al_agents::_windows'
  include_recipe 'al_agents::_linux'

The _linux recipe is

::Chef::Recipe.send(:include, AlAgents::Helpers)

include_recipe 'al_agents::selinux' if selinux_enabled?
include_recipe 'al_agents::rsyslog' if rsyslog_detected?

The windows is

::Chef::Recipe.send(:include, AlAgents::Helpers)
::Chef::Resource.send(:include, AlAgents::Helpers)

cache_dir = Chef::Config[:file_cache_path]
cached_package = ::File.join(cache_dir, agent_basename)

Focus on the

::Chef::Recipe.send(:include, AlAgents::Helpers)
::Chef::Resource.send(:include, AlAgents::Helpers)

This code is basically saying “extend the Chef::Recipe and Chef::Resource class and add the following methods to it”. Any instance of type Chef::Resource will have the methods in AlAgents::Helpers available to call!

AlAgents::Helpers does indeed have a service_name method

Simplifying this do the following in a recipe

puts self.methods.grep(/service/) #=> no service name on windows Chef Client 12.8.1 service, macosx_service, windows_service

::Chef::Recipe.send(:include, AlAgents::Helpers)
::Chef::Resource.send(:include, AlAgents::Helpers)

puts self.methods.grep(/service/) # => includes service_name

In case you hadn’t noticed any code you write in your Chef libraries that aren’t namespaced are automatically included in Chef::Recipe and Chef::Resource which make is really simple for you to call your methods.

I’m conflicted about this type of coding practice, while it makes using methods simpler it means you can lose your code or it can be overwritten by code loaded later in the compile cycle. This is because all Classes in Ruby are open classes (thank goodness as we’ve monkey patched a lot of the Chef base classes to find issues), methods are defined as they are reached by the interpreter and most developers add their methods on a blanket basis i.e.

::Chef::Recipe.send(:include, AlAgents::Helpers)
::Chef::Resource.send(:include, AlAgents::Helpers)

Rather than

::Chef::Resource::Batch.send(:include, AlAgents::Helpers) # => Just extend batch resources

Extend just this class do not change the definition of Chef::Recipe



Thanks, that makes sense now. This is on windows. As a work around I did rename my resources so they don’t conflict.


Related with the topic name, I am having a similar issue:

resource_name :redis
default_action :install

property :user, String

def user(arg = nil)
  set_or_return(:user, arg, default: node['redis']['user'])

action :install do
  user 'redis' do
    username new_resource.user
    action :create

the user resource is replaced with String

What am I missing?