Puppet::Resource::Type represents nodes, classes and defined types.
It has a standard format for external consumption, usable from the resource_type indirection via rest and the resource_type face. See the {file:api_docs/http_resource_type.md#Schema resource type schema description}.
@api public
Map the names used in our documentation to the names used internally
This should probably be renamed to ‘kind’ eventually, in accordance with the changes
made for serialization and API usability (#14137). At the moment that seems like it would touch a whole lot of places in the code, though. --cprice 2012-04-23
# File lib/puppet/resource/type.rb, line 241 def assign_parameter_values(parameters, resource) return unless parameters # It'd be nice to assign default parameter values here, # but we can't because they often rely on local variables # created during set_resource_parameters. parameters.each do |name, value| resource.set_parameter name, value end end
Are we a child of the passed class? Do a recursive search up our parentage tree to figure it out.
# File lib/puppet/resource/type.rb, line 100 def child_of?(klass) return false unless parent return(klass == parent_type ? true : parent_type.child_of?(klass)) end
Make an instance of the resource type, and place it in the catalog if it isn’t in the catalog already. This is only possible for classes and nodes. No parameters are be supplied–if this is a parameterized class, then all parameters take on their default values.
# File lib/puppet/resource/type.rb, line 200 def ensure_in_catalog(scope, parameters=nil) type == :definition and raise ArgumentError, "Cannot create resources for defined resource types" resource_type = type == :hostclass ? :class : :node # Do nothing if the resource already exists; this makes sure we don't # get multiple copies of the class resource, which helps provide the # singleton nature of classes. # we should not do this for classes with parameters # if parameters are passed, we should still try to create the resource # even if it exists so that we can fail # this prevents us from being able to combine param classes with include if resource = scope.catalog.resource(resource_type, name) and !parameters return resource end resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self) assign_parameter_values(parameters, resource) instantiate_resource(scope, resource) scope.compiler.add_resource(scope, resource) resource end
Now evaluate the code associated with this class or definition.
# File lib/puppet/resource/type.rb, line 107 def evaluate_code(resource) static_parent = evaluate_parent_type(resource) scope = static_parent || resource.scope scope = scope.newscope(:namespace => namespace, :source => self, :resource => resource) unless resource.title == :main scope.compiler.add_class(name) unless definition? set_resource_parameters(resource, scope) resource.add_edge_to_stage if code if @match # Only bother setting up the ephemeral scope if there are match variables to add into it begin elevel = scope.ephemeral_level scope.ephemeral_from(@match, file, line) code.safeevaluate(scope) ensure scope.unset_ephemeral_var(elevel) end else code.safeevaluate(scope) end end evaluate_ruby_code(resource, scope) if ruby_code end
# File lib/puppet/resource/type.rb, line 221 def instantiate_resource(scope, resource) # 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 if ['Class', 'Node'].include? resource.type scope.catalog.tag(*resource.tags) end end
This is only used for node names, and really only when the node name is a regexp.
# File lib/puppet/resource/type.rb, line 159 def match(string) return string.to_s.downcase == name unless name_is_regex? @match = @name.match(string) end
Add code from a new instance to our code.
# File lib/puppet/resource/type.rb, line 166 def merge(other) fail "#{name} is not a class; cannot add code to it" unless type == :hostclass fail "#{other.name} is not a class; cannot add code from it" unless other.type == :hostclass fail "Cannot have code outside of a class/node/define because 'freeze_main' is enabled" if name == "" and Puppet.settings[:freeze_main] if parent and other.parent and parent != other.parent fail "Cannot merge classes with different parent classes (#{name} => #{parent} vs. #{other.name} => #{other.parent})" end # We know they're either equal or only one is set, so keep whichever parent is specified. self.parent ||= other.parent if other.doc self.doc ||= "" self.doc += other.doc end # This might just be an empty, stub class. return unless other.code unless self.code self.code = other.code return end self.code = Puppet::Parser::ParserFactory.code_merger.concatenate([self, other]) # self.code = self.code.sequence_with(other.code) end
# File lib/puppet/resource/type.rb, line 232 def name return @name unless @name.is_a?(Regexp) @name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'') end
# File lib/puppet/resource/type.rb, line 237 def name_is_regex? @name.is_a?(Regexp) end
MQR TODO:
The change(s) introduced by the fix for #4270 are mostly silly & should be removed, though we didn’t realize it at the time. If it can be established/ ensured that nodes never call #parent_type and that resource_types are always (as they should be) members of exactly one #resource_type_collection the following method could / should be replaced with:
def #parent_type
@parent_type ||= parent && (
resource_type_collection.find_or_load([name],parent,type.to_sym) ||
fail Puppet::ParseError, "Could not find parent resource type '#{parent}' of type #{type} in #{resource_type_collection.environment}"
)
end
…and then the rest of the changes around passing in scope reverted.
# File lib/puppet/resource/type.rb, line 269 def parent_type(scope = nil) return nil unless parent unless @parent_type raise "Must pass scope to parent_type when called first time" unless scope unless @parent_type = scope.environment.known_resource_types.send("find_#{type}", [name], parent) fail Puppet::ParseError, "Could not find parent resource type '#{parent}' of type #{type} in #{scope.environment}" end end @parent_type end
Sets the argument name to Puppet Type hash used for type checking. Names must correspond to available arguments (they must be defined first). Arguments not mentioned will not be type-checked. Only supported when parser == “future”
# File lib/puppet/resource/type.rb, line 342 def set_argument_types(name_to_type_hash) @argument_types = {} # Stop here if not running under future parser, the rest requires pops to be initialized # and that the type system is available return unless Puppet.future_parser? && name_to_type_hash name_to_type_hash.each do |name, t| # catch internal errors unless @arguments.include?(name) raise Puppet::DevError, "Parameter '#{name}' is given a type, but is not a valid parameter." end unless t.is_a? Puppet::Pops::Types::PAnyType raise Puppet::DevError, "Parameter '#{name}' is given a type that is not a Puppet Type, got #{t.class}" end @argument_types[name] = t end end
# File lib/puppet/resource/type.rb, line 327 def set_arguments(arguments) @arguments = {} return if arguments.nil? arguments.each do |arg, default| arg = arg.to_s warn_if_metaparam(arg, default) @arguments[arg] = default end end
Set any arguments passed by the resource as variables in the scope.
# File lib/puppet/resource/type.rb, line 283 def set_resource_parameters(resource, scope) set = {} resource.to_hash.each do |param, value| param = param.to_sym fail Puppet::ParseError, "#{resource.ref} does not accept attribute #{param}" unless valid_parameter?(param) exceptwrap { scope[param.to_s] = value } set[param] = true end if @type == :hostclass scope["title"] = resource.title.to_s.downcase unless set.include? :title scope["name"] = resource.name.to_s.downcase unless set.include? :name else scope["title"] = resource.title unless set.include? :title scope["name"] = resource.name unless set.include? :name end scope["module_name"] = module_name if module_name and ! set.include? :module_name if caller_name = scope.parent_module_name and ! set.include?(:caller_module_name) scope["caller_module_name"] = caller_name end scope.class_set(self.name,scope) if hostclass? or node? # Evaluate the default parameters, now that all other variables are set default_params = resource.set_default_parameters(scope) default_params.each { |param| scope[param] = resource[param] } # This has to come after the above parameters so that default values # can use their values resource.validate_complete end
# File lib/puppet/resource/type.rb, line 76 def to_data_hash data = [:doc, :line, :file, :parent].inject({}) do |hash, param| next hash unless (value = self.send(param)) and (value != "") hash[param.to_s] = value hash end # External documentation uses "parameters" but the internal name # is "arguments" # Dump any arguments as source data['parameters'] = Hash[arguments.map do |k,v| [k, v.respond_to?(:source_text) ? v.source_text : v] end] data['name'] = name unless RESOURCE_KINDS_TO_EXTERNAL_NAMES.has_key?(type) raise ArgumentError, "Unsupported resource kind '#{type}'" end data['kind'] = RESOURCE_KINDS_TO_EXTERNAL_NAMES[type] data end
Check whether a given argument is valid.
# File lib/puppet/resource/type.rb, line 318 def valid_parameter?(param) param = param.to_s return true if param == "name" return true if Puppet::Type.metaparam?(param) return false unless defined?(@arguments) return(arguments.include?(param) ? true : false) end
# File lib/puppet/resource/type.rb, line 54 def self.from_data_hash(data) name = data.delete('name') or raise ArgumentError, "Resource Type names must be specified" kind = data.delete('kind') || "definition" unless type = RESOURCE_EXTERNAL_NAMES_TO_KINDS[kind] raise ArgumentError, "Unsupported resource kind '#{kind}'" end data = data.inject({}) { |result, ary| result[ary[0].intern] = ary[1]; result } # External documentation uses "parameters" but the internal name # is "arguments" data[:arguments] = data.delete(:parameters) new(type, name, data) end
# File lib/puppet/resource/type.rb, line 71 def self.from_pson(data) Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end
# File lib/puppet/resource/type.rb, line 136 def initialize(type, name, options = {}) @type = type.to_s.downcase.to_sym raise ArgumentError, "Invalid resource supertype '#{type}'" unless RESOURCE_KINDS.include?(@type) name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName) set_name_and_namespace(name) [:code, :doc, :line, :file, :parent].each do |param| next unless value = options[param] send(param.to_s + "=", value) end set_arguments(options[:arguments]) set_argument_types(options[:argument_types]) @match = nil @module_name = options[:module_name] end