How to access custom resource property values outside actions?


#1

Here’s a stripped back custom resource to illustrate my problem:

property :helloWorld, String

test2 = "we said [#{helloWorld}]"

action :create do
  Chef::Log.fatal(">>>>>>>>>>>>>#{test2}")
end

I want to be able to use the property values to build various strings to use inside the actions of the custom resource.

When I use this resource in a recipe, the error I get is:

undefined local variable or method `helloWorld’ for #Class:0x3fc4a40

If I wrap the test2 line in a load_current_value block, and make test2 an instance variable (@test2), then there are no errors but the value of property helloWorld is “”.

My guesses are that there is some sort of scope issue, or I need to instantiate a new_resource, or I need to do some sort of LWRP Provider style coding. Where am I going wrong and what have I missed out?

Thanks in advance.
Jim


#2

You are creating a local variable (test2) in the scope of the class definition, which means that you’re gonna attempt to call a method helloWorld on the class instead of an instance of the class.


#3

Hi Daniel

Thanks for the reply. Please excuse our lack of ruby knowledge. How would we define variables that can be consumed by different actions is our custom resource?

We are using the patten in the stripped down example to create common variables such as powershell scripts that we then use in a number of actions for our custom resources.

We were about to try calling a library function inside the actions to return the formatted variables, but had hoped to defines them as variables at the top of the resource to keep our code a little cleaner.

Thanks again for the response

Dave (Jims college)


#4

Hi Dave,

Some recipes in the wild use plain ruby methods in helpers or the custom resource themselves for building common strings.

So in your case it could be like:

property :helloWorld, String

def test2
   "we said [#{helloWorld}]"
end

action :create do
Chef::Log.fatal(">>>>>>>>>>>>>#{test2}")
end

#5

I have the same problem actually. Very new to Chef, I guess I’m just not used to the logic yet, but I keep thinking there must be a way to access the resource properties from load_current_value.

What I am trying to do here is my recipe would define a property for a SSH key file location, that I use in load_current_value to fetch and populate the current state of the resource.

I’m probably doing it wrong, but I would appreciate some guidance on how to do this. The project I’m working on is integrating appliances that do not have native chef client by using a proxy chef client for each. I would be using SSH or native appliance APIs to fetch current state and provide convergence baseline. I would like each appliance to be a node in chef, hence I’m currently running one chef client per appliance in cron, each with its own key and config file.

Thanks for your help!

EDIT : Some sample of what I’m doing :

My Recipe :

cluster '10.78.1.10' do
  username "chef"
  keyfile "cluster1"

  dns_servers '1.2.3.5'

  action :set_dns
end

My custom resource :

resource_name :cluster

property :ip_address, String, name_property: true
property :username, String
property :keyfile, String

property :cluster_name, String
property :dns_servers, String

load_current_value do

  # Cluster name
  io = IO.popen("ssh -i /root/cluster1 chef@#{ip_address} cluster identity show")
  out = io.read

  cluster_name out.sub!(/.*Cluster Name: (.*?)\r.*/m,'\1')

  # DNS for cluster

  io = IO.popen("ssh -i /root/cluster1 chef@#{ip_address} dns show -vserver #{cluster_name}")
  out = io.read

  dns_servers out.sub!(/.*Name Servers: (.*?)\r.*/m,'\1')

end

action :set_dns do
  converge_if_changed :dns_servers do
      IO.popen("ssh -i /root/#{keyfile} #{username}@#{ip_address} dns modify -vserver #{cluster_name} -name-servers #{dns_servers}")
  end
end

#6

You can access resource properties from load_current_value by adding a block argument:

load_current_value do |desired|
  puts "#{desired.cluster_name}"
end

Also properties that are marked desired_state:false or identity: true are preserved in load_current_value and are available to read in load_current_value - everything else gets reset, thus the need for a block. This blog post fills in some of the gaps in the Chef documentation around this concept:
http://blog.backslasher.net/chef-custom-resources.html


#7

P.S. Forgot to put quotes around the string in my original post:

load_current_value do |desired|
  puts "#{desired.cluster_name}"
end

#8

Oh boy… desired_state:false is exactly what I needed. I couldn’t make sense of the documentation for this, I found it quite confusing.
So basically, what it means is “don’t try to figure out the current value” ? I’m not sure I really get it now, but it looks like it’s doing what I want it to do!

Thank you so much!