Description

There is a difference in how Kernel.load() and require interact when referencing the same Ruby file – at least the file type – under an MRI puppet master vs. Puppet Server, both running 3.7.1 core Ruby Puppet code and 1.9-based Ruby.

During settings initialization, both masters will perform a:

Kernel.load(".../lib/puppet/type/file.rb", nil)

This causes the Puppet::Type::File type to be defined, as well as several related types, e.g., Puppet::Type::File::Owner, by virtue of "file.rb" doing require 'puppet/type/file/*' calls.

Assuming a custom module which includes a require 'puppet/type/file' call in some Ruby code is in place, the behavior when that line is exercised under MRI Ruby Puppet vs. Puppet Server during a catalog request differs.

Under the MRI Ruby Puppet master, the file require call does not result in the "file.rb" file being re-executed.

Under Puppet Server, however, the file require call does result in the "file.rb" file being re-executed. The Puppet::Type::File type is subsequently redefined, causing the subordinate types like Puppet::Type::File::Owner to no longer be defined. The "file.rb" file executes the require 'puppet/type/file/... calls again but because JRuby's loader interprets the associated files as already having been executed, the files are not executed again and the Puppet::Type::File::* types are not redefined. Any subsequent attempts to reference these subordinate types in code would, therefore, result in an error.

I suspect this may be showing a difference between how the caching behind Kernel.load() and Kernel.require() is linked under MRI Ruby vs. JRuby 1.9. This will probably involve further research into the loaders for those respective runtimes.

To demonstrate this problem, I created the following custom function and placed it under "<confdir>/<modules>/<modname>/lib/puppet/parser/functions/owner_defed_after_require.rb" on the server:

require 'puppet/type/file'

module Puppet::Parser::Functions

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

Puppet.info "owner_defed_after_require: enter"

klass_defined = false

if Object.const_defined?("Puppet")

klass = Object.const_get("Puppet")

if (klass.const_defined?("Type"))

klass = klass.const_get("Type")

if (klass.const_defined?("File"))

klass_defined = klass.const_get("File").const_defined?("Owner")

end

end

end

Puppet.info "owner_defed_after_require: exit (#{klass_defined})"

klass_defined

end

end

On the same server, I stored the following at "<confdir>/manifests/site.pp":

notify { 'owner_defed_after_require':

message => owner_defed_after_require()

}

When I performed a catalog request to the master, I saw "true" for the owner_defed_after_require message in the response for the MRI Ruby master vs. "false" for the same message in the response for the Puppet Server master.