class Puppet::Resource::Type

Puppet::Resource::Type represents nodes, classes and defined types.

@api public

Constants

CALLER_MODULE_NAME
DOUBLE_COLON
EMPTY_ARRAY
KIND
MODULE_NAME
NAME
NODES
PARAMETERS
RESOURCE_EXTERNAL_NAMES_TO_KINDS
RESOURCE_KINDS
RESOURCE_KINDS_TO_EXTERNAL_NAMES

Map the names used in our documentation to the names used internally

TITLE

Attributes

argument_types[R]

Map from argument (aka parameter) names to Puppet Type @return [Hash<Symbol, Puppet::Pops::Types::PAnyType] map from name to type

arguments[R]
behaves_like[R]
code[RW]
doc[RW]
file[RW]
line[RW]
module_name[R]
namespace[R]
override[RW]
parent[RW]
resource_type_collection[RW]
type[R]

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

Public Class Methods

new(type, name, options = {}) click to toggle source
    # File lib/puppet/resource/type.rb
 85 def initialize(type, name, options = {})
 86   @type = type.to_s.downcase.to_sym
 87   raise ArgumentError, _("Invalid resource supertype '%{type}'") % { type: type } unless RESOURCE_KINDS.include?(@type)
 88 
 89   name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName)
 90 
 91   set_name_and_namespace(name)
 92 
 93   [:code, :doc, :line, :file, :parent].each do |param|
 94     value = options[param]
 95     next unless value
 96     send(param.to_s + '=', value)
 97   end
 98 
 99   set_arguments(options[:arguments])
100   set_argument_types(options[:argument_types])
101 
102   @match = nil
103 
104   @module_name = options[:module_name]
105 end

Public Instance Methods

child_of?(klass) click to toggle source

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
53 def child_of?(klass)
54   return true if override
55   return false unless parent
56 
57   return(klass == parent_type ? true : parent_type.child_of?(klass))
58 end
ensure_in_catalog(scope, parameters=nil) click to toggle source

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
153 def ensure_in_catalog(scope, parameters=nil)
154   resource_type =
155   case type
156   when :definition
157     raise ArgumentError, _('Cannot create resources for defined resource types')
158   when :hostclass
159     :class
160   when :node
161     :node
162   end
163 
164   # Do nothing if the resource already exists; this makes sure we don't
165   # get multiple copies of the class resource, which helps provide the
166   # singleton nature of classes.
167   # we should not do this for classes with parameters
168   # if parameters are passed, we should still try to create the resource
169   # even if it exists so that we can fail
170   # this prevents us from being able to combine param classes with include
171   if parameters.nil?
172     resource = scope.catalog.resource(resource_type, name)
173     return resource unless resource.nil?
174   elsif parameters.is_a?(Hash)
175     parameters = parameters.map {|k, v| Puppet::Parser::Resource::Param.new(:name => k, :value => v, :source => self)}
176   end
177   resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self, :parameters => parameters)
178   instantiate_resource(scope, resource)
179   scope.compiler.add_resource(scope, resource)
180   resource
181 end
evaluate_code(resource) click to toggle source

Now evaluate the code associated with this class or definition.

   # File lib/puppet/resource/type.rb
61 def evaluate_code(resource)
62 
63   static_parent = evaluate_parent_type(resource)
64   scope = static_parent || resource.scope
65 
66   scope = scope.newscope(:source => self, :resource => resource) unless resource.title == :main
67   scope.compiler.add_class(name) unless definition?
68 
69   set_resource_parameters(resource, scope)
70 
71   resource.add_edge_to_stage
72 
73   if code
74     if @match # Only bother setting up the ephemeral scope if there are match variables to add into it
75       scope.with_guarded_scope do
76         scope.ephemeral_from(@match, file, line)
77         code.safeevaluate(scope)
78       end
79     else
80       code.safeevaluate(scope)
81     end
82   end
83 end
instantiate_resource(scope, resource) click to toggle source
    # File lib/puppet/resource/type.rb
183 def instantiate_resource(scope, resource)
184   # Make sure our parent class has been evaluated, if we have one.
185   if parent && !scope.catalog.resource(resource.type, parent)
186     parent_type(scope).ensure_in_catalog(scope)
187   end
188 
189   if ['Class', 'Node'].include? resource.type
190     scope.catalog.merge_tags_from(resource)
191   end
192 end
match(string) click to toggle source

This is only used for node names, and really only when the node name is a regexp.

    # File lib/puppet/resource/type.rb
109 def match(string)
110   return string.to_s.downcase == name unless name_is_regex?
111 
112   @match = @name.match(string)
113 end
merge(other) click to toggle source

Add code from a new instance to our code.

    # File lib/puppet/resource/type.rb
116 def merge(other)
117   fail _("%{name} is not a class; cannot add code to it") % { name: name } unless type == :hostclass
118   fail _("%{name} is not a class; cannot add code from it") % { name: other.name } unless other.type == :hostclass
119   if name == "" && Puppet.settings[:freeze_main]
120     # It is ok to merge definitions into main even if freeze is on (definitions are nodes, classes, defines, functions, and types)
121     unless other.code.is_definitions_only?
122       fail _("Cannot have code outside of a class/node/define because 'freeze_main' is enabled")
123     end
124   end
125   if parent and other.parent and parent != other.parent
126     fail _("Cannot merge classes with different parent classes (%{name} => %{parent} vs. %{other_name} => %{other_parent})") % { name: name, parent: parent, other_name: other.name, other_parent: other.parent }
127   end
128 
129   # We know they're either equal or only one is set, so keep whichever parent is specified.
130   self.parent ||= other.parent
131 
132   if other.doc
133     self.doc ||= ""
134     self.doc += other.doc
135   end
136 
137   # This might just be an empty, stub class.
138   return unless other.code
139 
140   unless self.code
141     self.code = other.code
142     return
143   end
144 
145   self.code = Puppet::Parser::ParserFactory.code_merger.concatenate([self, other])
146 end
name() click to toggle source
    # File lib/puppet/resource/type.rb
194 def name
195   if type == :node && name_is_regex?
196     "__node_regexp__#{@name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'')}"
197   else
198     @name
199   end
200 end
name_is_regex?() click to toggle source
    # File lib/puppet/resource/type.rb
202 def name_is_regex?
203   @name.is_a?(Regexp)
204 end
parent_type(scope = nil) click to toggle source
    # File lib/puppet/resource/type.rb
206 def parent_type(scope = nil)
207   return nil unless parent
208 
209   @parent_type ||= scope.environment.known_resource_types.send("find_#{type}", parent) ||
210     fail(Puppet::ParseError, _("Could not find parent resource type '%{parent}' of type %{parent_type} in %{env}") % { parent: parent, parent_type: type, env: scope.environment })
211 end
set_argument_types(name_to_type_hash) click to toggle source

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.

    # File lib/puppet/resource/type.rb
329 def set_argument_types(name_to_type_hash)
330   @argument_types = {}
331   @parameter_struct = nil
332   return unless name_to_type_hash
333   name_to_type_hash.each do |name, t|
334     # catch internal errors
335     unless @arguments.include?(name)
336       raise Puppet::DevError, _("Parameter '%{name}' is given a type, but is not a valid parameter.") % { name: name }
337     end
338     unless t.is_a? Puppet::Pops::Types::PAnyType
339       raise Puppet::DevError, _("Parameter '%{name}' is given a type that is not a Puppet Type, got %{class_name}") % { name: name, class_name: t.class }
340     end
341     @argument_types[name] = t
342   end
343 end
set_arguments(arguments) click to toggle source
    # File lib/puppet/resource/type.rb
313 def set_arguments(arguments)
314   @arguments = {}
315   @parameter_struct = nil
316   return if arguments.nil?
317 
318   arguments.each do |arg, default|
319     arg = arg.to_s
320     warn_if_metaparam(arg, default)
321     @arguments[arg] = default
322   end
323 end
set_resource_parameters(resource, scope) click to toggle source

Validate and set any arguments passed by the resource as variables in the scope.

This method is known to only be used on the server/compile side.

@param resource [Puppet::Parser::Resource] the resource @param scope [Puppet::Parser::Scope] the scope

@api private

    # File lib/puppet/resource/type.rb
221 def set_resource_parameters(resource, scope)
222   # Inject parameters from using external lookup
223   modname = resource[:module_name] || module_name
224   scope[MODULE_NAME] = modname unless modname.nil?
225   caller_name = resource[:caller_module_name] || scope.parent_module_name
226   scope[CALLER_MODULE_NAME] = caller_name unless caller_name.nil?
227 
228   inject_external_parameters(resource, scope)
229 
230   if @type == :hostclass
231     scope[TITLE] = resource.title.to_s.downcase
232     scope[NAME] =  resource.name.to_s.downcase
233   else
234     scope[TITLE] = resource.title
235     scope[NAME] =  resource.name
236   end
237   scope.class_set(self.name,scope) if hostclass? || node?
238 
239   param_hash = scope.with_parameter_scope(resource.to_s, arguments.keys) do |param_scope|
240     # Assign directly to the parameter scope to avoid scope parameter validation at this point. It
241     # will happen anyway when the values are assigned to the scope after the parameter scoped has
242     # been popped.
243     resource.each { |k, v| param_scope[k.to_s] = v.value unless k == :name || k == :title }
244     assign_defaults(resource, param_scope, scope)
245     param_scope.to_hash
246   end
247 
248   validate_resource_hash(resource, param_hash)
249 
250   # Assign parameter values to current scope
251   param_hash.each { |param, value| exceptwrap { scope[param] = value }}
252 end
valid_parameter?(param) click to toggle source

Check whether a given argument is valid.

    # File lib/puppet/resource/type.rb
309 def valid_parameter?(param)
310   parameter_struct.hashed_elements.include?(param.to_s)
311 end
validate_resource(resource) click to toggle source

Validate that all parameters given to the resource are correct @param resource [Puppet::Resource] the resource to validate

    # File lib/puppet/resource/type.rb
296 def validate_resource(resource)
297   # Since Sensitive values have special encoding (in a separate parameter) an unwrapped sensitive value must be
298   # recreated as a Sensitive in order to perform correct type checking.
299   sensitives = Set.new(resource.sensitive_parameters)
300   validate_resource_hash(resource,
301     Hash[resource.parameters.map do |name, value|
302       value_to_validate = sensitives.include?(name) ? Puppet::Pops::Types::PSensitiveType::Sensitive.new(value.value) : value.value
303       [name.to_s, value_to_validate]
304     end
305   ])
306 end

Private Instance Methods

assign_defaults(resource, param_scope, scope) click to toggle source
    # File lib/puppet/resource/type.rb
274 def assign_defaults(resource, param_scope, scope)
275   return unless resource.is_a?(Puppet::Parser::Resource)
276   parameters = resource.parameters
277   arguments.each do |param_name, default|
278     next if default.nil?
279     name = param_name.to_sym
280     param = parameters[name]
281     next unless param.nil? || param.value.nil?
282     value = exceptwrap { param_scope.evaluate3x(param_name, default, scope) }
283     resource[name] = value
284     param_scope[param_name] = value
285   end
286 end
convert_from_ast(name) click to toggle source
    # File lib/puppet/resource/type.rb
347 def convert_from_ast(name)
348   value = name.value
349   if value.is_a?(Puppet::Parser::AST::Regex)
350     value.value
351   else
352     value
353   end
354 end
create_params_struct() click to toggle source
    # File lib/puppet/resource/type.rb
404 def create_params_struct
405   arg_types = argument_types
406   type_factory = Puppet::Pops::Types::TypeFactory
407   members = { type_factory.optional(type_factory.string(NAME)) =>  type_factory.any }
408 
409   Puppet::Type.eachmetaparam do |name|
410     # TODO: Once meta parameters are typed, this should change to reflect that type
411     members[name.to_s] = type_factory.any
412   end
413 
414   arguments.each_pair do |name, default|
415     key_type = type_factory.string(name.to_s)
416     key_type = type_factory.optional(key_type) unless default.nil?
417 
418     arg_type = arg_types[name]
419     arg_type = type_factory.any if arg_type.nil?
420     members[key_type] = arg_type
421   end
422   type_factory.struct(members)
423 end
evaluate_parent_type(resource) click to toggle source
    # File lib/puppet/resource/type.rb
356 def evaluate_parent_type(resource)
357   klass = parent_type(resource.scope)
358   parent_resource = resource.scope.compiler.catalog.resource(:class, klass.name) || resource.scope.compiler.catalog.resource(:node, klass.name) if klass
359   return unless klass && parent_resource
360   parent_resource.evaluate unless parent_resource.evaluated?
361   parent_scope(resource.scope, klass)
362 end
inject_external_parameters(resource, scope) click to toggle source

Lookup and inject parameters from external scope @param resource [Puppet::Parser::Resource] the resource @param scope [Puppet::Parser::Scope] the scope

    # File lib/puppet/resource/type.rb
257 def inject_external_parameters(resource, scope)
258   # Only lookup parameters for host classes
259   return unless type == :hostclass
260   parameters = resource.parameters
261   arguments.each do |param_name, default|
262     sym_name = param_name.to_sym
263     param = parameters[sym_name]
264     next unless param.nil? || param.value.nil?
265     catch(:no_such_key) do
266       bound_value = Puppet::Pops::Lookup.search_and_merge("#{name}::#{param_name}", Puppet::Pops::Lookup::Invocation.new(scope), nil)
267       # Assign bound value but don't let an undef trump a default expression
268       resource[sym_name] = bound_value unless bound_value.nil? && !default.nil?
269     end
270   end
271 end
namesplit(fullname) click to toggle source

Split an fq name into a namespace and name

    # File lib/puppet/resource/type.rb
365 def namesplit(fullname)
366   ary = fullname.split(DOUBLE_COLON)
367   n = ary.pop || ""
368   ns = ary.join(DOUBLE_COLON)
369   return ns, n
370 end
parameter_struct() click to toggle source
    # File lib/puppet/resource/type.rb
400 def parameter_struct
401   @parameter_struct ||= create_params_struct
402 end
parent_scope(scope, klass) click to toggle source
    # File lib/puppet/resource/type.rb
372 def parent_scope(scope, klass)
373   scope.class_scope(klass) || raise(Puppet::DevError, _("Could not find scope for %{class_name}") % { class_name: klass.name })
374 end
set_name_and_namespace(name) click to toggle source
    # File lib/puppet/resource/type.rb
376 def set_name_and_namespace(name)
377   if name.is_a?(Regexp)
378     @name = name
379     @namespace = ""
380   else
381     @name = name.to_s.downcase
382 
383     # Note we're doing something somewhat weird here -- we're setting
384     # the class's namespace to its fully qualified name.  This means
385     # anything inside that class starts looking in that namespace first.
386     @namespace, _ = @type == :hostclass ? [@name, ''] : namesplit(@name)
387   end
388 end
validate_resource_hash(resource, resource_hash) click to toggle source
    # File lib/puppet/resource/type.rb
289 def validate_resource_hash(resource, resource_hash)
290   Puppet::Pops::Types::TypeMismatchDescriber.validate_parameters(resource.to_s, parameter_struct, resource_hash, false)
291 end
warn_if_metaparam(param, default) click to toggle source
    # File lib/puppet/resource/type.rb
390 def warn_if_metaparam(param, default)
391   return unless Puppet::Type.metaparamclass(param)
392 
393   if default
394     warnonce _("%{param} is a metaparam; this value will inherit to all contained resources in the %{name} definition") % { param: param, name: self.name }
395   else
396     raise Puppet::ParseError, _("%{param} is a metaparameter; please choose another parameter name in the %{name} definition") % { param: param, name: self.name }
397   end
398 end