Puppet Version: Seen in Puppet 5 and 6 Puppet Server Version: N/A OS Name/Version: RHEL 7 If a custom function defines a Ruby module also used by Puppet (ie. module JSON), and other loaded modules attempt to use related functions (ie. JSON functions from stdlib that call JSON), they fail because Puppet runs the custom function code within the Puppet::Pops::Loader::RubyFunctionInstantiator scope instead of top scope:
2020-03-18T00:00:29.762-05:00 ERROR [qtp802169583-480691] [puppetserver] Puppet Evaluation Error: Error while evaluating a Function Call, undefined method `pretty_generate' for Puppet::Pops::Loader::RubyFunctionInstantiator::JSON:Module (file: /etc/puppetlabs/code/environments/production/modules/npc_coreservices/manifests/components/cache_proxy/install.pp, line: 106, column: 22) on node pv20616cspgl01.npc.lan |
/etc/puppetlabs/code/environments/production/modules/stdlib/lib/puppet/functions/to_json_pretty.rb:36:in `to_json_pretty' |
/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/functions/dispatch.rb:60:in `invoke' |
/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/functions/dispatcher.rb:43:in `block in dispatch' |
org/jruby/RubyKernel.java:1180:in `catch' |
/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/functions/dispatcher.rb:42:in `dispatch' |
/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/functions/function.rb:46:in `block in call' |
org/jruby/RubyKernel.java:1180:in `catch' |
/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/functions/function.rb:45:in `call' |
/etc/puppetlabs/code/environments/production/modules/npc_coreservices/manifests/components/cache_proxy/install.pp:106:in `<eval>' |
org/jruby/RubyKernel.java:1037:in `eval' |
...
|
For example, a Puppet module with a custom Ruby function that declares:
require 'json' |
|
module JSON |
...
|
and another Puppet module that attempts to prettify JSON via stdlib:
$json = to_json_pretty($some_json_content)
|
can produce the above error. For another example, see: https://github.com/sensu/puppet-module-sensuclassic/issues/24 and the patch to work around it: https://github.com/sensu/puppet-module-sensuclassic/pull/25/files Desired Behavior: Users can define a Ruby JSON module in a custom function if necessary without breaking default behavior. Actual Behavior: stdlib doesn't explicitly defines the correct scope when invoking JSON (MODULES ticket tbd), so an eval() statement in RubyFunctionInstantiator executes the code within its scope instead. https://github.com/puppetlabs/puppet/blob/master/lib/puppet/pops/loader/ruby_function_instantiator.rb#L22 If custom Ruby functions cannot be guaranteed to run in the expected scope, or if module declarations are unsafe, this behavior should at a minimum be documented, and if possible the error messages should indicate the scope conflict. |