Eric Young Eric Young - 3 months ago 18
Ruby Question

How do I pass a hash to a custom function in puppet?

I have defined a custom function currently based on the very simple example here: https://docs.puppet.com/guides/custom_functions.html

module Puppet::Parser::Functions
newfunction(:transform_service_hash) do |args|
filename = args[0]
hash_to_be_transformed = args[1]
File.open(filename, 'a') {|fd| fd.puts hash_to_be_transformed }
end
end


This kinda works. I can call it like this:

$my_hash = { key => "value1" , key2 => "value2" }
notify{ "new hash!! $my_hash" :}
transform_service_hash('/var/tmp/blah',$my_hash)


and the file displays:

mgt21 ~ # cat /var/tmp/blah
keyvalue1key2value2


But, if I try to access elements of the hash, nothing changes:

module Puppet::Parser::Functions
newfunction(:transform_service_hash) do |args|
filename = args[0]
hash_to_be_transformed = args[1]
element1 = hash_to_be_transformed["key"]
File.open(filename, 'a') {|fd| fd.puts element1 }
end
end


The above block outputs the exact same data to /var/tmp/blah.


And, interestingly, if I remove the filename pass and define it statically in the module:

$my_hash = { key => "value1" , key2 => "value2" }
notify{ "new hash!! $my_hash. element1 is: $my_hash.key" :}
transform_service_hash($my_hash)


and

module Puppet::Parser::Functions
newfunction(:transform_service_hash) do |args|
hash_to_be_transformed = args[0]
element1 = hash_to_be_transformed["key"]
File.open('/var/tmp/blah2', 'a') {|fd| fd.puts element1 }
end
end


I get the following error: "Error 400 on SERVER: can't convert Hash into String" with a line reference pointing to "transform_service_hash($my_hash)"

I am new to both puppet and ruby...so I'm unsure I am not passing the element properly, if I am not receiving it properly, or if it something that puppet cannot handle. Please note that I am using version 3.8 of puppet and 1.8.7 of ruby.

Thanks for any help. I've been banging my head against this, and google hasn't been forthcoming yet.

---Edit to clarify my goals (I also edited my code a bit for specificity): I am attempting to pass a hash into a custom ruby function within puppet. The "test" hash has two elements: one string and one array. It is defined as such:

$my_hash = { key => "value1" , key2 => ['array_value1', 'array_value2'] }
$my_display_element=$my_hash["key2"][0]
notify{ "new hash!! $my_hash. the first value of the array stored in element2 is: $my_display_element" :}
transform_service_hash('/var/tmp/blah',$my_hash)


The function appears like so:

module Puppet::Parser::Functions
newfunction(:transform_service_hash) do |args|
filename = args[0]
hash_to_be_transformed = args[1]
element1 = args[1]["key"]
element2 = args[1]["key2"][0]
#element1 = hash_to_be_transformed["key"]
#element2 = hash_to_be_transformed["key2"][0]
File.open(filename, 'a') {|fd| fd.puts "hash_to_be_transformed: #{hash_to_be_transformed}\n" }
File.open(filename, 'a') {|fd| fd.puts "element1: #{element1}\n" }
File.open(filename, 'a') {|fd| fd.puts "element2: #{element2}\n" }
end
end


For now, I just want to be able to see that I am able to access elements within the passed hash like a hash. So I'd love for the output file to look like:

hash_to_be_transformed: keyvalue1key2array_value1array_value2
element1: value1
element2: array_value1


However, in the output file, I see:

mgt21 ~ # cat /var/tmp/blah
keyvalue1key2array_value1array_value2


Clearly, something is off here as my text is not being added and the full hash is just printed out just once and seemingly in string form.

I believe that this may be related to the error that I get when I don't pass in a file name (see above). I think that my hash is getting interpreted (or passed) as a string and, as such, I am unable to access the elements. Unfortunately, I still have been unable to verify this or figure out why it might be happening.

---Edit2 based on Matt's answer below.

I decided to simplify my code to isolate this "can't convert Hash into String error". I also made his suggested changes to remove the ambiguity from my key declarations.

$my_hash = { 'key' => "value1" , 'key2' => ['array_value1', 'array_value2'] }
$my_display_element=$my_hash["key2"][0]
notify{ "new hash!! $my_hash. the first value of the array stored in element2 is: $my_display_element" :}
transform_service_hash($my_hash)


and

module Puppet::Parser::Functions
newfunction(:transform_service_hash) do |args|
hash_to_be_transformed = args[0]
element1 = args[0]['key']
element2 = args[0]['key2'][0]
File.open('/var/tmp/blah', 'a') {|fd| fd.puts "hash_to_be_transformed: #{hash_to_be_transformed}\n" }
File.open('/var/tmp/blah', 'a') {|fd| fd.puts "element1: #{element1}\n" }
File.open('/var/tmp/blah', 'a') {|fd| fd.puts "element2: #{element2}\n" }
end
end


But, I still end up with the same "Hash to String error". It is worth noting that I also tried simplifying my hash to:

$my_hash = { 'key' => "value1" , 'key2' => "value2" }


and I still get the "Hash to String error".

Answer

After much gnashing of teeth (and some very helpful pointers from @MattSchuchard), I realized that none of the changes to my function were going into effect. One needs to restart the puppetmaster service after each change to a custom function: docs.puppet.com/guides/custom_functions.html (appropriately under "Gotchas").

Once I started restarting this service after each change to the function, my hash was able to be parsed properly:

from the .pp file:

  $filename = "/var/tmp/test"
  $my_hash = { 'key' => "value1" , 'key2' => ["M\'lady\n*doffs cap*", 'array_value2'] }
  transform_service_hash($filename, $my_hash)

from the ruby file:

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    filename = args[0]
    hash_to_be_transformed = args[1]
    array_val = hash_to_be_transformed['key2'][0]
    File.open(filename, 'a') {|fd| fd.puts "#{array_val}\n" }
  end
end

and output:

mgt21 tmp # cat test 
M'lady
*doffs cap*
Comments