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
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
# File lib/puppet/resource/type.rb 86 def initialize(type, name, options = {}) 87 @type = type.to_s.downcase.to_sym 88 raise ArgumentError, _("Invalid resource supertype '%{type}'") % { type: type } unless RESOURCE_KINDS.include?(@type) 89 90 name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName) 91 92 set_name_and_namespace(name) 93 94 [:code, :doc, :line, :file, :parent].each do |param| 95 value = options[param] 96 next unless value 97 send(param.to_s + '=', value) 98 end 99 100 set_arguments(options[:arguments]) 101 set_argument_types(options[:argument_types]) 102 103 @match = nil 104 105 @module_name = options[:module_name] 106 end
Public Instance Methods
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 54 def child_of?(klass) 55 return true if override 56 return false unless parent 57 58 return(klass == parent_type ? true : parent_type.child_of?(klass)) 59 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 154 def ensure_in_catalog(scope, parameters=nil) 155 resource_type = 156 case type 157 when :definition 158 raise ArgumentError, _('Cannot create resources for defined resource types') 159 when :hostclass 160 :class 161 when :node 162 :node 163 end 164 165 # Do nothing if the resource already exists; this makes sure we don't 166 # get multiple copies of the class resource, which helps provide the 167 # singleton nature of classes. 168 # we should not do this for classes with parameters 169 # if parameters are passed, we should still try to create the resource 170 # even if it exists so that we can fail 171 # this prevents us from being able to combine param classes with include 172 if parameters.nil? 173 resource = scope.catalog.resource(resource_type, name) 174 return resource unless resource.nil? 175 elsif parameters.is_a?(Hash) 176 parameters = parameters.map {|k, v| Puppet::Parser::Resource::Param.new(:name => k, :value => v, :source => self)} 177 end 178 resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self, :parameters => parameters) 179 instantiate_resource(scope, resource) 180 scope.compiler.add_resource(scope, resource) 181 resource 182 end
Now evaluate the code associated with this class or definition.
# File lib/puppet/resource/type.rb 62 def evaluate_code(resource) 63 64 static_parent = evaluate_parent_type(resource) 65 scope = static_parent || resource.scope 66 67 scope = scope.newscope(:source => self, :resource => resource) unless resource.title == :main 68 scope.compiler.add_class(name) unless definition? 69 70 set_resource_parameters(resource, scope) 71 72 resource.add_edge_to_stage 73 74 if code 75 if @match # Only bother setting up the ephemeral scope if there are match variables to add into it 76 scope.with_guarded_scope do 77 scope.ephemeral_from(@match, file, line) 78 code.safeevaluate(scope) 79 end 80 else 81 code.safeevaluate(scope) 82 end 83 end 84 end
# File lib/puppet/resource/type.rb 184 def instantiate_resource(scope, resource) 185 # Make sure our parent class has been evaluated, if we have one. 186 if parent && !scope.catalog.resource(resource.type, parent) 187 parent_type(scope).ensure_in_catalog(scope) 188 end 189 190 if ['Class', 'Node'].include? resource.type 191 scope.catalog.merge_tags_from(resource) 192 end 193 end
This is only used for node names, and really only when the node name is a regexp.
# File lib/puppet/resource/type.rb 110 def match(string) 111 return string.to_s.downcase == name unless name_is_regex? 112 113 @match = @name.match(string) 114 end
Add code from a new instance to our code.
# File lib/puppet/resource/type.rb 117 def merge(other) 118 fail _("%{name} is not a class; cannot add code to it") % { name: name } unless type == :hostclass 119 fail _("%{name} is not a class; cannot add code from it") % { name: other.name } unless other.type == :hostclass 120 if name == "" && Puppet.settings[:freeze_main] 121 # It is ok to merge definitions into main even if freeze is on (definitions are nodes, classes, defines, functions, and types) 122 unless other.code.is_definitions_only? 123 fail _("Cannot have code outside of a class/node/define because 'freeze_main' is enabled") 124 end 125 end 126 if parent and other.parent and parent != other.parent 127 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 } 128 end 129 130 # We know they're either equal or only one is set, so keep whichever parent is specified. 131 self.parent ||= other.parent 132 133 if other.doc 134 self.doc ||= "" 135 self.doc += other.doc 136 end 137 138 # This might just be an empty, stub class. 139 return unless other.code 140 141 unless self.code 142 self.code = other.code 143 return 144 end 145 146 self.code = Puppet::Parser::ParserFactory.code_merger.concatenate([self, other]) 147 end
# File lib/puppet/resource/type.rb 195 def name 196 if type == :node && name_is_regex? 197 "__node_regexp__#{@name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'')}" 198 else 199 @name 200 end 201 end
# File lib/puppet/resource/type.rb 203 def name_is_regex? 204 @name.is_a?(Regexp) 205 end
# File lib/puppet/resource/type.rb 207 def parent_type(scope = nil) 208 return nil unless parent 209 210 @parent_type ||= scope.environment.known_resource_types.send("find_#{type}", parent) || 211 fail(Puppet::ParseError, _("Could not find parent resource type '%{parent}' of type %{parent_type} in %{env}") % { parent: parent, parent_type: type, env: scope.environment }) 212 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.
# File lib/puppet/resource/type.rb 330 def set_argument_types(name_to_type_hash) 331 @argument_types = {} 332 @parameter_struct = nil 333 return unless name_to_type_hash 334 name_to_type_hash.each do |name, t| 335 # catch internal errors 336 unless @arguments.include?(name) 337 raise Puppet::DevError, _("Parameter '%{name}' is given a type, but is not a valid parameter.") % { name: name } 338 end 339 unless t.is_a? Puppet::Pops::Types::PAnyType 340 raise Puppet::DevError, _("Parameter '%{name}' is given a type that is not a Puppet Type, got %{class_name}") % { name: name, class_name: t.class } 341 end 342 @argument_types[name] = t 343 end 344 end
# File lib/puppet/resource/type.rb 314 def set_arguments(arguments) 315 @arguments = {} 316 @parameter_struct = nil 317 return if arguments.nil? 318 319 arguments.each do |arg, default| 320 arg = arg.to_s 321 warn_if_metaparam(arg, default) 322 @arguments[arg] = default 323 end 324 end
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 222 def set_resource_parameters(resource, scope) 223 # Inject parameters from using external lookup 224 modname = resource[:module_name] || module_name 225 scope[MODULE_NAME] = modname unless modname.nil? 226 caller_name = resource[:caller_module_name] || scope.parent_module_name 227 scope[CALLER_MODULE_NAME] = caller_name unless caller_name.nil? 228 229 inject_external_parameters(resource, scope) 230 231 if @type == :hostclass 232 scope[TITLE] = resource.title.to_s.downcase 233 scope[NAME] = resource.name.to_s.downcase 234 else 235 scope[TITLE] = resource.title 236 scope[NAME] = resource.name 237 end 238 scope.class_set(self.name,scope) if hostclass? || node? 239 240 param_hash = scope.with_parameter_scope(resource.to_s, arguments.keys) do |param_scope| 241 # Assign directly to the parameter scope to avoid scope parameter validation at this point. It 242 # will happen anyway when the values are assigned to the scope after the parameter scoped has 243 # been popped. 244 resource.each { |k, v| param_scope[k.to_s] = v.value unless k == :name || k == :title } 245 assign_defaults(resource, param_scope, scope) 246 param_scope.to_hash 247 end 248 249 validate_resource_hash(resource, param_hash) 250 251 # Assign parameter values to current scope 252 param_hash.each { |param, value| exceptwrap { scope[param] = value }} 253 end
Check whether a given argument is valid.
# File lib/puppet/resource/type.rb 310 def valid_parameter?(param) 311 parameter_struct.hashed_elements.include?(param.to_s) 312 end
Validate that all parameters given to the resource are correct @param resource [Puppet::Resource] the resource to validate
# File lib/puppet/resource/type.rb 297 def validate_resource(resource) 298 # Since Sensitive values have special encoding (in a separate parameter) an unwrapped sensitive value must be 299 # recreated as a Sensitive in order to perform correct type checking. 300 sensitives = Set.new(resource.sensitive_parameters) 301 validate_resource_hash(resource, 302 Hash[resource.parameters.map do |name, value| 303 value_to_validate = sensitives.include?(name) ? Puppet::Pops::Types::PSensitiveType::Sensitive.new(value.value) : value.value 304 [name.to_s, value_to_validate] 305 end 306 ]) 307 end
Private Instance Methods
# File lib/puppet/resource/type.rb 275 def assign_defaults(resource, param_scope, scope) 276 return unless resource.is_a?(Puppet::Parser::Resource) 277 parameters = resource.parameters 278 arguments.each do |param_name, default| 279 next if default.nil? 280 name = param_name.to_sym 281 param = parameters[name] 282 next unless param.nil? || param.value.nil? 283 value = exceptwrap { param_scope.evaluate3x(param_name, default, scope) } 284 resource[name] = value 285 param_scope[param_name] = value 286 end 287 end
# File lib/puppet/resource/type.rb 348 def convert_from_ast(name) 349 value = name.value 350 if value.is_a?(Puppet::Parser::AST::Regex) 351 value.value 352 else 353 value 354 end 355 end
# File lib/puppet/resource/type.rb 405 def create_params_struct 406 arg_types = argument_types 407 type_factory = Puppet::Pops::Types::TypeFactory 408 members = { type_factory.optional(type_factory.string(NAME)) => type_factory.any } 409 410 Puppet::Type.eachmetaparam do |name| 411 # TODO: Once meta parameters are typed, this should change to reflect that type 412 members[name.to_s] = type_factory.any 413 end 414 415 arguments.each_pair do |name, default| 416 key_type = type_factory.string(name.to_s) 417 key_type = type_factory.optional(key_type) unless default.nil? 418 419 arg_type = arg_types[name] 420 arg_type = type_factory.any if arg_type.nil? 421 members[key_type] = arg_type 422 end 423 type_factory.struct(members) 424 end
# File lib/puppet/resource/type.rb 357 def evaluate_parent_type(resource) 358 klass = parent_type(resource.scope) 359 parent_resource = resource.scope.compiler.catalog.resource(:class, klass.name) || resource.scope.compiler.catalog.resource(:node, klass.name) if klass 360 return unless klass && parent_resource 361 parent_resource.evaluate unless parent_resource.evaluated? 362 parent_scope(resource.scope, klass) 363 end
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 258 def inject_external_parameters(resource, scope) 259 # Only lookup parameters for host classes 260 return unless type == :hostclass 261 parameters = resource.parameters 262 arguments.each do |param_name, default| 263 sym_name = param_name.to_sym 264 param = parameters[sym_name] 265 next unless param.nil? || param.value.nil? 266 catch(:no_such_key) do 267 bound_value = Puppet::Pops::Lookup.search_and_merge("#{name}::#{param_name}", Puppet::Pops::Lookup::Invocation.new(scope), nil) 268 # Assign bound value but don't let an undef trump a default expression 269 resource[sym_name] = bound_value unless bound_value.nil? && !default.nil? 270 end 271 end 272 end
Split an fq name into a namespace and name
# File lib/puppet/resource/type.rb 366 def namesplit(fullname) 367 ary = fullname.split(DOUBLE_COLON) 368 n = ary.pop || "" 369 ns = ary.join(DOUBLE_COLON) 370 return ns, n 371 end
# File lib/puppet/resource/type.rb 401 def parameter_struct 402 @parameter_struct ||= create_params_struct 403 end
# File lib/puppet/resource/type.rb 373 def parent_scope(scope, klass) 374 scope.class_scope(klass) || raise(Puppet::DevError, _("Could not find scope for %{class_name}") % { class_name: klass.name }) 375 end
# File lib/puppet/resource/type.rb 377 def set_name_and_namespace(name) 378 if name.is_a?(Regexp) 379 @name = name 380 @namespace = "" 381 else 382 @name = name.to_s.downcase 383 384 # Note we're doing something somewhat weird here -- we're setting 385 # the class's namespace to its fully qualified name. This means 386 # anything inside that class starts looking in that namespace first. 387 @namespace, _ = @type == :hostclass ? [@name, ''] : namesplit(@name) 388 end 389 end
# File lib/puppet/resource/type.rb 290 def validate_resource_hash(resource, resource_hash) 291 Puppet::Pops::Types::TypeMismatchDescriber.validate_parameters(resource.to_s, parameter_struct, resource_hash, false) 292 end
# File lib/puppet/resource/type.rb 391 def warn_if_metaparam(param, default) 392 return unless Puppet::Type.metaparamclass(param) 393 394 if default 395 warnonce _("%{param} is a metaparam; this value will inherit to all contained resources in the %{name} definition") % { param: param, name: self.name } 396 else 397 raise Puppet::ParseError, _("%{param} is a metaparameter; please choose another parameter name in the %{name} definition") % { param: param, name: self.name } 398 end 399 end