The Puppet Labs Issue Tracker has Moved: https://tickets.puppetlabs.com

Bug #2930

endless loop when calling function from within custom function

Added by konrad rzentarzewski over 4 years ago. Updated over 2 years ago.

Status:AcceptedStart date:12/14/2009
Priority:NormalDue date:
Assignee:-% Done:

0%

Category:functions
Target version:3.x
Affected Puppet version:0.25.1 Branch:
Keywords:puppetmaster hangs in endless loop

We've Moved!

Ticket tracking is now hosted in JIRA: https://tickets.puppetlabs.com

This ticket may be automatically exported to the PUP project on JIRA using the button below:


Description

based on [http://reductivelabs.com/trac/puppet/wiki/WritingYourOwnFunctions] it should be possible to call custom functions (including template and include functions) from within own function. however in 0.25.1 this leads to endless loops, consuming all system memory on puppetmaster.

below are the 2 functions. 1st one causes loop and exhausts all resources, while 2nd one runs without problems.

Puppet::Parser::Functions::newfunction(:template_search, :type => :rvalue, :doc =>
    "Find first existing file and run puppet template function on it. If you want
    to concatenate multiple templates try var = template_search(file1,file2) +
    template_search(file3,file4) and then use var in your resources.") do |vals|
            ret = nil
            errors = Array.new
            vals.each do |file|
                debug "in :template_search find %s" % file
                ret = nil
                begin
                    ret = function_template(file)
                rescue Exception=>e
                    if /could.not.find.template/i =~ e
                        debug "in :template_search not found %s" % file
                    else
                        raise Puppet::ParseError, "in :template_search exception in %s: %s" % [file,e]
                        errors.push(e)
                    end
                end
                if ret
                    debug "in :template_search found %s" % file
                    break
                end
            end
            if ret
                ret
            else
                if errors.length > 0
                    raise Puppet::ParseError, "Errors from :template function: %s" %
                        errors.join(", ")
                else
                    raise Puppet::ParseError, "Could not find any files from %s" %
                        vals.join(", ")
                end
            end
end
Puppet::Parser::Functions::newfunction(:template_search, :type => :rvalue, :doc =>
    "Find first existing file and run puppet template function on it. If you want
    to concatenate multiple templates try var = template_search(file1,file2) +
    template_search(file3,file4) and then use var in your resources.") do |vals|
            require 'erb'
 
            ret = nil
            errors = Array.new
            vals.each do |file|
                debug "in :template_search find %s" % file
                ret = nil
                begin
                    wrapper = Puppet::Parser::TemplateWrapper.new(self)
                    wrapper.file = file
                    ret = wrapper.result
                rescue Exception=>e
                    if /could.not.find.template/i =~ e
                        debug "in :template_search not found %s" % file
                    else
                        raise Puppet::ParseError, "in :template_search exception in %s: %s" % [file,e]
                        errors.push(e)
                    end
                end
                if ret
                    debug "in :template_search found %s" % file
                    break
                end
            end
            if ret
                ret
            else
                if errors.length > 0
                    raise Puppet::ParseError, "Errors from :template function: %s" %
                        errors.join(", ")
                else
                    raise Puppet::ParseError, "Could not find any files from %s" %
                        vals.join(", ")
                end
            end
end

History

#1 Updated by Markus Roberts over 4 years ago

  • Status changed from Unreviewed to Investigating
  • Assignee set to Markus Roberts

I don’t see off hand what’s causing the problem but there are a number of odd things about these functions; for example, in:

 raise Puppet::ParseError, "in :template_search exception in %s: %s" % [file,e]
 errors.push(e)

the “errors.push(e)” will never be reached.

#2 Updated by konrad rzentarzewski over 4 years ago

yes, i’m not ruby black belt :) however 2nd form (just without function_template call) works just right. can you replicate this problem?

#3 Updated by Markus Roberts over 4 years ago

  • Status changed from Investigating to Rejected

When I try I get the expected:

err: in :template_search exception in 2930.pp: undefined method `function_template' for
#<Puppet::Parser::Scope:0x22b747c> at /Users/markus/projects/puppet/2930.pp:2 on node phage.home

Feel free to reopen the ticket (or a new one) if you find a circumstance in which it still happens.

#4 Updated by konrad rzentarzewski over 4 years ago

does @template@ function works from your manifests? according to docs (see original submission) template and inline functions should be accessible from within custom functions (with function_funcname interface). i think this needs clarification (whether it’s possible to call those).

#5 Updated by konrad rzentarzewski over 4 years ago

comments?

#6 Updated by Yuri Arabadji about 3 years ago

  • Status changed from Rejected to Re-opened

I can confirm that inline functions are not accessible from within custom functions:

module Puppet::Parser::Functions
    newfunction(:file_template, :type => :rvalue) do |args|
        [skip]

        function_template([template_path])
    end
end

undefined method `function_template' for #<Puppet::Parser::Scope:0x7fb48eb7c588> at [skip]

puppet 2.6.7.

UPdate: netiher are custom functions accessible until I “require” them explicitly, but then comes the autoloading problem: err: Could not retrieve catalog from remote server: Error 400 on SERVER: Could not autoload get_template_path: Function file_exists already defined at /etc/puppet/development/modules/snmpd/manifests/init.pp:42

#7 Updated by Yuri Arabadji about 3 years ago

Here’s a workaround for custom functions. Haven’t tried with built-ins.

Right inside module declaration, call function(‘the_function_you_want_to_use’) method:

module Puppet::Parser::Functions
  function('custom_function')

  newfunction(:blah, ...) do |args|
    function_custom_function(['param'])
  end
end

#8 Updated by Greg Swift almost 3 years ago

Yuri Arabadji wrote:

Here’s a workaround for custom functions. Haven’t tried with built-ins.

Right inside module declaration, call function(‘the_function_you_want_to_use’) method:

[…]

does this workaround only work in 2.6.7+ ? I’m on 2.6.6 and am getting the ‘undefined method’ error, even with the work around. Attempting to require them specifically has had no effect, but like Konrad, I’m not a ruby ninja, so could be doing that wrong.

UPDATE: using the require method described in a blog I found, this works.

http://holyhandgrenade.org/blog/2011/03/calling-custom-functions-from-other-custom-functions-in-puppet/trackback/

  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

#9 Updated by hai wu almost 3 years ago

I hit this bug today, I am using puppet server/client 2.6.9-1. The workaround function(‘function_name’) works for me.

#10 Updated by James Turnbull over 2 years ago

  • Status changed from Re-opened to Needs Decision
  • Assignee changed from Markus Roberts to Nigel Kersten

This is an ugly UX problem.

#11 Updated by Nigel Kersten over 2 years ago

  • Status changed from Needs Decision to Accepted
  • Assignee deleted (Nigel Kersten)
  • Target version set to 3.x

Also available in: Atom PDF