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

Bug #15756

scope.function_template with a single String parameter fails in Ruby 1.9

Added by Roger Que over 2 years ago. Updated over 1 year ago.

Status:ClosedStart date:07/31/2012
Priority:NormalDue date:
Assignee:-% Done:

0%

Category:templates
Target version:3.0.0
Affected Puppet version:2.7.18 Branch:https://github.com/puppetlabs/puppet/pull/1105
Keywords:ruby19 functions customer

We've Moved!

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

This issue is currently not available for export. If you are experiencing the issue described below, please file a new ticket in JIRA. Once a new ticket has been created, please add a link to it that points back to this Redmine ticket.


Description

The Puppet documentation mentions using scope.function_template with a String parameter in order to render an ERB template inside another template:

<%= scope.function_template('referenced_template.erb') %>

This works in Ruby 1.8, but fails with the following error when the Puppet master is running 1.9:

err: Could not retrieve catalog from remote server: Error 400 on SERVER: Failed to parse template [xxx].erb:
  Filepath: /usr/share/ruby/vendor_ruby/puppet/parser/functions/template.rb
  Line: 10
  Detail: undefined method `collect' for "referenced_template.erb":String
 at /etc/puppet/modules/[yyy].pp:164 on node [zzz.example]

It looks like the template function is invoking collect on its argument regardless of whether it’s a String or Array, which works in Ruby 1.8 ('abc'.collect behaves like ['abc'].collect) but not in 1.9. Passing an Array works with both versions.

This is on Fedora 17, Ruby 1.9.3p194, with Puppet 2.7.18.


Related issues

Related to Puppet - Feature #15989: It should be possible to specify arity on custom functions Closed 08/15/2012
Related to Puppet - Bug #4915: scope.function_extlookup (in templates) takes an array pa... Closed 10/01/2010
Related to Puppet - Feature #21048: error about functions with single array mentions only use... Accepted

History

#1 Updated by James Turnbull over 2 years ago

  • Keywords set to ruby19

#2 Updated by Josh Cooper over 2 years ago

  • Status changed from Unreviewed to Accepted
  • Assignee set to eric sorenson
  • Target version set to 3.x

Some functions correctly handle being called with either a string or array, e.g. defined.

    vals = [vals] unless vals.is_a?(Array)
    vals.each do |val|

Others do not, and will need to be fixed for ruby19 support. At quick glance these look suspect:

file
inline_template
md5
regsubst
search
sha1
shellquote
sprintf
tagged
template

Also, the include function flattens arrays, which none of the other functions do.

Since ruby19 is not officially supported in 2.7.x http://docs.puppetlabs.com/guides/platforms.html#ruby-versions I’m targeting this at 3.x. Eric, should this be for telly?

#3 Updated by Josh Cooper over 2 years ago

  • Target version changed from 3.x to 3.0.0
  • Keywords changed from ruby19 to ruby19 functions

Marking this for telly based on conversations with Andy

#5 Updated by Anonymous over 2 years ago

From what I understand a function should always be expecting an array. The example that is given works in ruby 1.8 because String had an #each method, whereas in ruby 1.9 String does not. The #each method was dropped in favor of #each_line which is much more explicit about what it will be iterating over, but also means that String is no longer Enumerable. So #function_template just happened to work because the string being passed has a single line that is the template to render.

In the end this really seems to be a documentation bug because the docs for writing functions state that the brackets for creating an array are required at the call site.

Instead of opening us up to more complicated semantics around when to convert things to arrays, we should probably add an assertion to the function calling code to make sure that a single array is being passed. That would catch errors like this much earlier.

#6 Updated by eric sorenson over 2 years ago

  • Assignee deleted (eric sorenson)

#7 Updated by eric sorenson over 2 years ago

  • Assignee set to Anonymous

Andy is working on a pull request to issue a warning when you call a function from ruby (either from other functions or inside a template) with a non-array arg.

#8 Updated by Anonymous over 2 years ago

  • Status changed from Accepted to In Topic Branch Pending Review
  • Branch set to https://github.com/puppetlabs/puppet/pull/1105

https://github.com/puppetlabs/puppet/pull/1105

#9 Updated by Anonymous over 2 years ago

  • Status changed from In Topic Branch Pending Review to Code Insufficient

The supplied code only tests the first time that a function is called, so will miss almost all cases that this should apply.

#10 Updated by Anonymous over 2 years ago

Oops, you are right. The check should be moved into where Puppet::Util::Functions converts the block into a method. At that point it can put a place a method that does the check then calls the block.

#11 Updated by Deepak Giridharagopal over 2 years ago

  • Status changed from Code Insufficient to In Topic Branch Pending Review

#12 Updated by Anonymous over 2 years ago

I still get test failures with the updated code:

  1) The require function should add a dependency between the 'required' class and our class
     Failure/Error: @scope.function_require("requiredclass")
     ArgumentError:
       custom functions must be called with a single array that contains the arguments
     # ./lib/puppet/parser/functions.rb:65:in `block in newfunction'
     # ./spec/integration/parser/functions/require_spec.rb:20:in `block (2 levels) in '

  2) The require function should queue relationships between the 'required' class and our classes
     Failure/Error: @scope.function_require("requiredclass1")
     ArgumentError:
       custom functions must be called with a single array that contains the arguments
     # ./lib/puppet/parser/functions.rb:65:in `block in newfunction'
     # ./spec/integration/parser/functions/require_spec.rb:31:in `block (2 levels) in '

  3) Puppet::Parser::Functions#hiera_array should raise a useful error when nil is returned
     Failure/Error: expect { scope.function_hiera_array("badkey") }.to raise_error(Puppet::ParseError, /Could not find data item badkey/ )
       expected Puppet::ParseError with message matching /Could not find data item badkey/, got #
     # ./spec/unit/parser/functions/hiera_array_spec.rb:16:in `block (2 levels) in '

  4) Puppet::Parser::Functions#hiera_hash should raise a useful error when nil is returned
     Failure/Error: expect { scope.function_hiera_hash("badkey") }.to raise_error(Puppet::ParseError, /Could not find data item badkey/ )
       expected Puppet::ParseError with message matching /Could not find data item badkey/, got #
     # ./spec/unit/parser/functions/hiera_hash_spec.rb:12:in `block (2 levels) in '

  5) Puppet::Parser::Functions#hiera_include should raise a useful error when nil is returned
     Failure/Error: expect { scope.function_hiera_include("badkey") }.to raise_error(Puppet::ParseError, /Could not find data item badkey/ )
       expected Puppet::ParseError with message matching /Could not find data item badkey/, got #
     # ./spec/unit/parser/functions/hiera_include_spec.rb:12:in `block (2 levels) in '

  6) Puppet::Parser::Functions#hiera should raise a useful error when nil is returned
     Failure/Error: expect { scope.function_hiera("badkey") }.to raise_error(Puppet::ParseError, /Could not find data item badkey/ )
       expected Puppet::ParseError with message matching /Could not find data item badkey/, got #
     # ./spec/unit/parser/functions/hiera_spec.rb:14:in `block (2 levels) in '

  7) Puppet::Parser::Functions when calling newfunction should create the function in the environment module
     Failure/Error: Puppet::Parser::Functions.newfunction("name", :type => :rvalue)
     Mocha::ExpectationError:
       unexpected invocation: #.define_method('real_function_name')
       unsatisfied expectations:
       - expected exactly once, not yet invoked: #.define_method()
       satisfied expectations:
       - allowed any number of times, not yet invoked: Signal.trap(any_parameters)
       - allowed any number of times, invoked once: Puppet::Parser::Functions.environment_module(any_parameters)
     # ./lib/puppet/parser/functions.rb:58:in `newfunction'
     # ./spec/unit/parser/functions_spec.rb:27:in `block (3 levels) in '

  8) Puppet::Parser::Functions when calling newfunction should warn if the function already exists
     Failure/Error: Puppet::Parser::Functions.newfunction("name", :type => :rvalue)
     Mocha::ExpectationError:
       unexpected invocation: #.define_method('real_function_name')
       unsatisfied expectations:
       - expected exactly twice, not yet invoked: #.define_method()
       satisfied expectations:
       - allowed any number of times, not yet invoked: Signal.trap(any_parameters)
       - allowed any number of times, invoked once: Puppet::Parser::Functions.environment_module(any_parameters)
     # ./lib/puppet/parser/functions.rb:58:in `newfunction'
     # ./spec/unit/parser/functions_spec.rb:32:in `block (3 levels) in '

  9) Puppet::Parser::Functions when calling function to test function existance should return its name if the function exists
     Failure/Error: Puppet::Parser::Functions.newfunction("name", :type => :rvalue)
     Mocha::ExpectationError:
       unexpected invocation: #.define_method('real_function_name')
       unsatisfied expectations:
       - expected exactly once, not yet invoked: #.define_method()
       satisfied expectations:
       - allowed any number of times, not yet invoked: Signal.trap(any_parameters)
       - allowed any number of times, invoked once: Puppet::Parser::Functions.environment_module(any_parameters)
     # ./lib/puppet/parser/functions.rb:58:in `newfunction'
     # ./spec/unit/parser/functions_spec.rb:59:in `block (3 levels) in '

#13 Updated by Anonymous over 2 years ago

  • Status changed from In Topic Branch Pending Review to Tests Insufficient

#14 Updated by Anonymous over 2 years ago

  • Status changed from Tests Insufficient to Merged - Pending Release

Works now, thanks.

#15 Updated by Anonymous over 2 years ago

I’ve submitted a PR against the docs to get the example corrected: https://github.com/puppetlabs/puppet-docs/pull/101

#17 Updated by Matthaus Owens about 2 years ago

  • Status changed from Merged - Pending Release to Closed

Released in Puppet 3.0.0-rc7

#18 Updated by Ryan Gordon almost 2 years ago

Would be awesome if the function and argument were shown in the error message. I never would have realized this was the problem and not the other custom ruby defines i had in my template file: <%= scope.function_template(“…”) %> to <%= scope.function_template([“…”]) %>

#19 Updated by Charlie Sharpsteen over 1 year ago

  • Keywords changed from ruby19 functions to ruby19 functions customer

Also available in: Atom PDF