module Puppet::Pops::Evaluator # Converts nested 4x supported values to 3x values. This is required because # resources and other objects do not know about the new type system, and does not support # regular expressions. Unfortunately this has to be done for array and hash as well. # A complication is that catalog types needs to be resolved against the scope. # # Users should not create instances of this class. Instead the class methods {Runtime3Converter.convert}, # {Runtime3Converter.map_args}, or {Runtime3Converter.instance} should be used class Runtime3Converter MAX_INTEGER = Puppet::Pops::MAX_INTEGER MIN_INTEGER = Puppet::Pops::MIN_INTEGER # Converts 4x supported values to a 3x values. Same as calling Runtime3Converter.instance.map_args(...) # # @param args [Array] Array of values to convert # @param scope [Puppet::Parser::Scope] The scope to use when converting # @param undef_value [Object] The value that nil is converted to # @return [Array] The converted values # def self.map_args(args, scope, undef_value) @instance.map_args(args, scope, undef_value) end # Converts 4x supported values to a 3x values. Same as calling Runtime3Converter.instance.convert(...) # # @param o [Object]The value to convert # @param scope [Puppet::Parser::Scope] The scope to use when converting # @param undef_value [Object] The value that nil is converted to # @return [Object] The converted value # def self.convert(o, scope, undef_value) @instance.convert(o, scope, undef_value) end # Returns the singleton instance of this class. # @return [Runtime3Converter] The singleton instance def self.instance @instance end # Converts 4x supported values to a 3x values. # # @param args [Array] Array of values to convert # @param scope [Puppet::Parser::Scope] The scope to use when converting # @param undef_value [Object] The value that nil is converted to # @return [Array] The converted values # def map_args(args, scope, undef_value) args.map {|a| convert(a, scope, undef_value) } end # Converts a 4x supported value to a 3x value. # # @param o [Object]The value to convert # @param scope [Puppet::Parser::Scope] The scope to use when converting # @param undef_value [Object] The value that nil is converted to # @return [Object] The converted value # def convert(o, scope, undef_value) @convert_visitor.visit_this_2(self, o, scope, undef_value) end def convert_NilClass(o, scope, undef_value) @inner ? nil : undef_value end def convert_Integer(o, scope, undef_value) return o unless o < MIN_INTEGER || o > MAX_INTEGER range_end = o > MAX_INTEGER ? 'max' : 'min' raise Puppet::Error, "Use of a Ruby Integer outside of Puppet Integer #{range_end} range, got '#{"0x%x" % o}'" end def convert_BigDecimal(o, scope, undef_value) # transform to same value float value if possible without any rounding error f = o.to_f return f unless f != o raise Puppet::Error, "Use of a Ruby BigDecimal value outside Puppet Float range, got '#{o}'" end def convert_String(o, scope, undef_value) # Although wasteful, a dup is needed because user code may mutate these strings when applying # Resources. This does not happen when in master mode since it only uses Resources that are # in puppet core and those are all safe. o.frozen? && !Puppet.run_mode.master? ? o.dup : o end def convert_Object(o, scope, undef_value) o end def convert_Array(o, scope, undef_value) ic = @inner_converter o.map {|x| ic.convert(x, scope, undef_value) } end def convert_Hash(o, scope, undef_value) result = {} ic = @inner_converter o.each {|k,v| result[ic.convert(k, scope, undef_value)] = ic.convert(v, scope, undef_value) } result end def convert_Iterator(o, scope, undef_value) raise Puppet::Error, _('Use of an Iterator is not supported here') end def convert_Symbol(o, scope, undef_value) o == :undef ? undef_value : o #o == :undef && !@inner ? undef_value : o end def convert_PAnyType(o, scope, undef_value) o end def convert_PCatalogEntryType(o, scope, undef_value) # Since 4x does not support dynamic scoping, all names are absolute and can be # used as is (with some check/transformation/mangling between absolute/relative form # due to Puppet::Resource's idiosyncratic behavior where some references must be # absolute and others cannot be. # Thus there is no need to call scope.resolve_type_and_titles to do dynamic lookup. t, title = catalog_type_to_split_type_title(o) t = Runtime3ResourceSupport.find_resource_type(scope, t) unless t == 'class' || t == 'node' Puppet::Resource.new(t, title) end # Produces an array with [type, title] from a PCatalogEntryType # This method is used to produce the arguments for creation of reference resource instances # (used when 3x is operating on a resource). # Ensures that resources are *not* absolute. # def catalog_type_to_split_type_title(catalog_type) split_type = catalog_type.is_a?(Puppet::Pops::Types::PTypeType) ? catalog_type.type : catalog_type case split_type when Puppet::Pops::Types::PClassType class_name = split_type.class_name ['class', class_name.nil? ? nil : class_name.sub(/^::/, '')] when Puppet::Pops::Types::PResourceType type_name = split_type.type_name title = split_type.title if type_name =~ /^(::)?[Cc]lass$/ ['class', title.nil? ? nil : title.sub(/^::/, '')] else # Ensure that title is '' if nil # Resources with absolute name always results in error because tagging does not support leading :: [type_name.nil? ? nil : type_name.sub(/^::/, '').downcase, title.nil? ? '' : title] end else #TRANSLATORS 'PClassType' and 'PResourceType' are Puppet types and should not be translated raise ArgumentError, _("Cannot split the type %{class_name}, it represents neither a PClassType, nor a PResourceType.") % { class_name: catalog_type.class } end end protected def initialize(inner = false) @inner = inner @inner_converter = inner ? self : self.class.new(true) @convert_visitor = Puppet::Pops::Visitor.new(self, 'convert', 2, 2) end @instance = self.new end # A Ruby function written for the 3.x API cannot be expected to handle extended data types. This # converter ensures that they are converted to String format # @api private class Runtime3FunctionArgumentConverter < Runtime3Converter def convert_Regexp(o, scope, undef_value) # Puppet 3x cannot handle parameter values that are regular expressions. Turn into regexp string in # source form o.inspect end def convert_Version(o, scope, undef_value) # Puppet 3x cannot handle SemVers. Use the string form o.to_s end def convert_VersionRange(o, scope, undef_value) # Puppet 3x cannot handle SemVerRanges. Use the string form o.to_s end def convert_Binary(o, scope, undef_value) # Puppet 3x cannot handle Binary. Use the string form o.to_s end def convert_Timespan(o, scope, undef_value) # Puppet 3x cannot handle Timespans. Use the string form o.to_s end def convert_Timestamp(o, scope, undef_value) # Puppet 3x cannot handle Timestamps. Use the string form o.to_s end # Converts result back to 4.x by replacing :undef with nil in Array and Hash objects # def self.convert_return(val3x) if val3x == :undef nil elsif val3x.is_a?(Array) val3x.map {|v| convert_return(v) } elsif val3x.is_a?(Hash) hsh = {} val3x.each_pair {|k,v| hsh[convert_return(k)] = convert_return(v)} hsh else val3x end end @instance = self.new end end