This post probably describes a bug, but I haven’t had the time yet to determine if this still exists in Puppet 2.6.x. Instead, here’s a post that will hopefully help out someone else having the same problem.
The other day, I was writing some custom parser functions for our Puppet 0.25.x install. In the interest of reusability, the idea was to keep the functions small and composable, and have them call each other in a nice, maintainable way in order to prevent code duplication.
As per the Puppet documentation, Puppet functions are called from Ruby by prefixing the function name with function_:
module Puppet::Parser::Functions newfunction(:my_function) do |args| # Puppet functions expect an array containing all of the arguments, # so we have to wrap our single string argument function_notice(["Called notice() from my_function()"]) end end
This worked great for calling built-in functions, but when I tried to call one of my own functions from another of my own functions, Puppet would just hang:
my_function.rb:
module Puppet::Parser::Functions newfunction(:my_function) do |args| function_my_other_function(args) end end
my_other_function.rb:
module Puppet::Parser::Functions newfunction(:my_other_function) # Never get here end end
After a bunch of debugging, I found that the Puppet autoloader seemed to be spinning itself into an infinite loop trying to locate and load my_other_function when it was called from my_function.rb. The solution was to manually require the file containing that function:
require File.join([File.expand_path(File.dirname(__FILE__)), 'my_other_function.rb']) module Puppet::Parser::Functions newfunction(:my_function) do |args| function_my_other_function(args) end end
The above assumes, of course, that the functions are in the same directory as one another.
With the dependency loaded, the custom function should work the same as any other parser function.
This an existing issue, #4549. I also wasted a bunch of time and independently found it as #5587 with 2.6. The later issue has some other workarounds that will still use the autoloader.
Also related to #2930. Your work around actually got me past the issue, thank you very much! Sadly, It just led to my next stumbling block, but each step is one step closer to success.
So once again my search for things brings me to your page (thank you!).
Unfortunately, what you says works for you is giving me an error. Using the function_notice you have above I was getting this error:
After a little bit of not understanding why it was throwing a join error I just wrapped the string in [] and it started working.
module Puppet::Parser::Functions newfunction(:my_function) do |args| function_notice(["Called notice() from my_function()"]) end endThen had the ‘ah duh’ understanding that all of these functions require a list input when called from inside other functions. I had run into this first time around with just the chaining of functions in general, and you show it above because you are passing the args list to the other functions.
Thanks for the heads-up; I was doing this post from memory and knew I’d slip up somewhere. Funny thing is I could swear I’ve fixed this post already. I’ve fixed the original post where I call function_notice().
Good reference about it here:
http://docs.puppetlabs.com/guides/custom_functions.html#calling-functions-from-functions
The documented method actually doesn’t work in many older versions of Puppet — it’s very easy to create infinite loops with the autoloader, especially in 0.25.x. Instead, we get explicit.and manually pull in the specific function definitions we need.