Ask Your Question
1

custom recursive function fails...

asked 2015-02-09 05:30:34 -0600

Krist van Besien gravatar image

I created a custom puppet function, with the following ruby code. (The idea is to do a depth first search in a hash).

module Puppet::Parser::Functions

  newfunction(:deep_find_with_breadcrumbs, :type => :rvalue) do |args|

    if (args.size != 3) then
      raise(Puppet::ParseError, "deep_find_with_breadcrumbs(): Wrong number of arguments "+
      "given #{args.size} for 3.")
    end

    myhash = args[0]
    mykey = args[1]
    myvalue = args[2]

    def deep_find(key, value, object, found=nil, crumbs=nil, crumb=nil )
      if crumb
        if crumbs
          crumbs += ( "," +  crumb )
        else
          crumbs = crumb
        end
      end
      if object.respond_to?(:key?) && object.key?(key) && object[key] == value
        return crumbs + "," + key
      elsif object.is_a? Hash
        object.find { |hkey,hvalue| found = deep_find(key, value, hvalue, found, crumbs, hkey )  }
        return found
      end
    end

    return deep_find(mykey,myvalue,myhash)

  end

end

However when I refer to this function in a puppet manifest I get the following error:

Error: Could not retrieve catalog from remote server: Error 400 on SERVER: undefined method `deep_find' for #<Puppet::Parser::Scope:0x7f0c87ac45f0> at <redacted>.pp:92 on node <redacted>
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run

This seems to indicated that hte method is not being defined, in spite of the "def" statement.

This is my first foray in to ruby. What did I do wrong?

edit retag flag offensive close merge delete

3 Answers

Sort by ยป oldest newest most voted
1

answered 2016-03-24 09:03:05 -0600

Henrik Lindberg gravatar image

updated 2016-03-24 09:03:54 -0600

Using def inside a Puppet 3.x function is not supported and must not be used as it taints runtime classes! You may later run into surprises and cause hard to find problems.

In puppet 4.x (and 3.x with future parser) there is a new API for functions called the 4.x function API. There it is trivial to create a recursive function, and it is safe to define additional helper methods.

If you must implement this using 3.x, you must place the logic outside of the function in a separate class and call it from the function.

edit flag offensive delete link more

Comments

Thanks for the heads up. That's good to know.

Alex Harvey gravatar imageAlex Harvey ( 2016-03-24 09:52:57 -0600 )edit

Is there documentation somewhere about this issue by the way?

Alex Harvey gravatar imageAlex Harvey ( 2016-03-24 09:58:12 -0600 )edit
Alex Harvey gravatar imageAlex Harvey ( 2016-03-25 03:39:13 -0600 )edit

You cannot have anything else but the function in your 3.x function file. The files representing functions may not define any new methods, classes/modules/constants, class instance variables etc. The typical is to deliver helper code in a class in the puppetx namespace.

Henrik Lindberg gravatar imageHenrik Lindberg ( 2016-03-25 18:13:36 -0600 )edit

You will be much happier with puppet 4.x where you can do what without effort.

Henrik Lindberg gravatar imageHenrik Lindberg ( 2016-03-25 18:14:08 -0600 )edit
0

answered 2016-03-23 09:39:43 -0600

updated 2016-03-23 23:44:01 -0600

I just stumbled upon the same issue.

I have found a few ways of doing this, and the best seems to be:

module Puppet::Parser::Functions
  newfunction(:deep_find_with_breadcrumbs, :type => :rvalue) do |args|
    extend Puppet::Parser::Functions

    def self.deep_find(key, value, object, found=nil, crumbs=nil, crumb=nil )
      if crumb
        if crumbs
          crumbs += ( "," +  crumb )
        else
          crumbs = crumb
        end
      end
      if object.respond_to?(:key?) && object.key?(key) && object[key] == value
        return crumbs + "," + key
      elsif object.is_a? Hash
        object.find { |hkey,hvalue| found = deep_find(key, value, hvalue, found, crumbs, hkey) }
        return found
      end
    end

    if (args.size != 3) then
      raise(Puppet::ParseError, "deep_find_with_breadcrumbs(): Wrong number of arguments "+
      "given #{args.size} for 3.")
    end

    myhash = args[0]
    mykey = args[1]
    myvalue = args[2]

    return Puppet::Parser::Functions.deep_find(mykey,myvalue,myhash)

  end

end

If you prefer to follow @GregLarkin's suggestion then you would need to also define it as another Puppet function, e.g.

module Puppet::Parser::Functions
  newfunction(:deep_find, :type => rvalue) do |args|
    key, value, object, found, crumbs, crumb = args
    ...
  end

  newfunction(:deep_find_with_breadcrumbs, :type => :rvalue) do |args|
    ...
    return function_deep_find([mykey, myvalue, myhash])
  end
end

Note that I wrapped the arguments to function_deep_find() in an array there.

edit flag offensive delete link more
0

answered 2015-02-10 11:12:00 -0600

GregLarkin gravatar image

I've never tried it with a recursive function, but I wonder if you need to prepend function_ to the deep_find call, as described in the custom function documentation: https://docs.puppetlabs.com/guides/custom_functions.html#calling-functions-from-functions

Please give that a try and post back here if you run into any problems.

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2015-02-09 05:30:34 -0600

Seen: 464 times

Last updated: Mar 24 '16