# frozen_string_literal: true require_relative 'param' module Puppet::Pops module Resource def self.register_ptypes(loader, ir) types = [Param, ResourceTypeImpl].map do |c| c.register_ptype(loader, ir) end types.each { |t| t.resolve(loader) } end class ResourceTypeImpl # Make instances of this class directly creatable from the Puppet Language # as object. # include Puppet::Pops::Types::PuppetObject # Instances of ResourceTypeImpl can be used as the type of a Puppet::Parser::Resource/Puppet::Resource when compiling # include Puppet::CompilableResourceType # Returns the Puppet Type for this instance. def self._pcore_type @ptype end def self.register_ptype(loader, ir) param_ref = Types::PTypeReferenceType.new('Puppet::Resource::Param') @ptype = Pcore.create_object_type(loader, ir, self, 'Puppet::Resource::ResourceType3', nil, { Types::KEY_NAME => Types::PStringType::NON_EMPTY, 'properties' => { Types::KEY_TYPE => Types::PArrayType.new(param_ref), Types::KEY_VALUE => EMPTY_ARRAY }, 'parameters' => { Types::KEY_TYPE => Types::PArrayType.new(param_ref), Types::KEY_VALUE => EMPTY_ARRAY }, 'title_patterns_hash' => { Types::KEY_TYPE => Types::POptionalType.new( Types::PHashType.new(Types::PRegexpType::DEFAULT, Types::PArrayType.new(Types::PStringType::NON_EMPTY)) ), Types::KEY_VALUE => nil }, 'isomorphic' => { Types::KEY_TYPE => Types::PBooleanType::DEFAULT, Types::KEY_VALUE => true }, 'capability' => { Types::KEY_TYPE => Types::PBooleanType::DEFAULT, Types::KEY_VALUE => false }, }, EMPTY_HASH, [Types::KEY_NAME]) end def eql?(other) other.is_a?(Puppet::CompilableResourceType) && @name == other.name end alias == eql? # Compares this type against the given _other_ (type) and returns -1, 0, or +1 depending on the order. # @param other [Object] the object to compare against (produces nil, if not kind of Type} # @return [-1, 0, +1, nil] produces -1 if this type is before the given _other_ type, 0 if equals, and 1 if after. # Returns nil, if the given _other_ is not a kind of Type. # @see Comparable # def <=>(other) # Order is only maintained against other types, not arbitrary objects. # The natural order is based on the reference name used when comparing return nil unless other.is_a?(Puppet::CompilableResourceType) # against other type instances. self.ref <=> other.ref end METAPARAMS = [ :noop, :schedule, :audit, :loglevel, :alias, :tag, :require, :subscribe, :before, :notify, :stage ].freeze # Speed up lookup METAPARAMSET = Set.new(METAPARAMS).freeze attr_reader :name attr_reader :properties attr_reader :parameters attr_reader :title_patterns_hash attr_reader :title_patterns def initialize(name, properties = EMPTY_ARRAY, parameters = EMPTY_ARRAY, title_patterns_hash = nil, isomorphic = true, capability = false) @name = name @properties = properties @parameters = parameters @title_patterns_hash = title_patterns_hash @isomorphic = isomorphic # ignore capability parameter # Compute attributes hash # Compute key_names (possibly compound key if there are multiple name vars). @attributes = {} @key_attributes = [] # Name to kind of attribute @attr_types = {} # Add all meta params METAPARAMS.each { |p| @attr_types[p] = :meta } @property_set = Set.new(properties.map do |p| symname = p.name.to_sym @attributes[symname] = p @key_attributes << symname if p.name_var @attr_types[symname] = :property symname end).freeze @param_set = Set.new(parameters.map do |p| symname = p.name.to_sym @attributes[symname] = p @key_attributes << symname if p.name_var @attr_types[symname] = :param symname end).freeze # API for title patterns is [ [regexp, [ [ [sym, ], [sym, ] ] ] ] ] # Where lambdas are optional. This resource type impl does not support lambdas # Note that the pcore file has a simpler hashmap that is post processed here # since the structure must have Symbol instances for names which the .pp representation # does not deliver. # @title_patterns = case @key_attributes.length when 0 # TechDebt: The case of specifying title patterns when having no name vars is unspecified behavior in puppet # Here it is silently ignored. nil when 1 if @title_patterns_hash.nil? [[/(.*)/m, [[@key_attributes.first]]]] else # TechDebt: The case of having one namevar and an empty title patterns is unspecified behavior in puppet. # Here, it may lead to an empty map which may or may not trigger the wanted/expected behavior. # @title_patterns_hash.map { |k, v| [k, v.map { |n| [n.to_sym] }] } end else if @title_patterns_hash.nil? || @title_patterns_hash.empty? # TechDebt: While title patterns must be specified when more than one is used, they do not have # to match/set them all since some namevars can be omitted (to support the use case in # the 'package' type where 'provider' attribute is handled as part of the key without being # set from the title. # raise Puppet::DevError, _("you must specify title patterns when there are two or more key attributes") end @title_patterns_hash.nil? ? [] : @title_patterns_hash.map { |k, v| [k, v.map { |n| [n.to_sym] }] } end end # Override CompilableResource inclusion def is_3x_ruby_plugin? false end # Answers if the parameter name is a parameter/attribute of this type # This is part of the Puppet::Type API # Check if used when compiling (it is triggered in an apply) # def valid_parameter?(name) @attributes.include?(name) || METAPARAMSET.include?(name) end # The type implementation of finish does a lot of things # * iterates over all parameters and calls post_compile on them if the parameter impl responds to post_compile # * validates the relationship parameters # # This implementation does nothing - it is assumed that the catalog is already validated # via the relationship validator (done late in the game). def finish # Do nothing. end # This is called on a resource type # it performs tagging if it is a Class or Node. # It also ensure the parent type is in the catalog, but that is weird since # resource types cannot really inherit def instantiate_resource(scope, resource) # Do nothing because nothing is needed when compiling. # This is what the Puppet::Type implementation does # None of this should be needed # # Make sure our parent class has been evaluated, if we have one. # if parent && !scope.catalog.resource(resource.type, parent) # parent_type(scope).ensure_in_catalog(scope) # end # This will never happen # if ['Class', 'Node'].include? resource.type # scope.catalog.tag(*resource.tags) # end end # Being isomorphic in puppet means that the resource is managing a state # (as opposed to a resource like Exec that is a function, possibly with side effect. # In a Ruby implementation of a resource type, @isomorphic = false is used to turn # off isomorphism, it is true by default. # This is part of the Puppet::Type API. # def isomorphic? @isomorphic end # Produces the names of the attributes that make out the unique id of a resource # def key_attributes @key_attributes end # Gives a type a chance to issue deprecations for parameters. # @param title [String] the title of the resource of this type # @param attributes [Array] the set parameters in the resource instance def deprecate_params(title, attributes) # TODO: Current API somewhat unclear, if done at type level, or per # Param. end ################################################## # NEVER CALLED COMPILE SIDE FOR A COMPILATION ################################################## # Answers :property, :param or :meta depending on the type of the attribute # According to original version, this is called millions of times # and a cache is required. # @param name [Symbol] def attrtype(name) raise NotImplementedError, "attrtype() - returns the kind (:meta, :param, or :property) of the parameter" # @attr_types[name] end # Returns the implementation of a param/property/attribute - i.e. a Param class def attrclass(name) raise NotImplementedError, "attrclass() - returns the (param) class of the parameter" end # PROBABLY NOT USED WHEN COMPILING # Returns the names of all attributes in a defined order: # * all key attributes (the composite id) # * :provider if it is specified # * all properties # * all parameters # * meta parameters # def allattrs raise NotImplementedError, "allattrs() - return all attribute names in order - probably not used master side" # key_attributes | (parameters & [:provider]) | properties.collect { |property| property.name } | parameters | metaparams end # Sets "applies to host" def apply_to raise NotImplementedError, "apply_to() - probably used when selecting a provider (device/host support)" end def apply_to_host raise NotImplementedError, "apply_to_host() - probably used when selecting a provider (device/host support)" end def apply_to_device raise NotImplementedError, "apply_to_device() - probably used when selecting a provider (device/host support)" end def apply_to_all raise NotImplementedError, "apply_to_all() - probably used when selecting a provider (device/host support)" end def can_apply_to_target(target) raise NotImplementedError, "can_apply_to_target() - probably used when selecting a provider (device/host support)" end end end end