class Puppet::Resource::Type

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

Constants

RESOURCE_EXTERNAL_NAMES_TO_KINDS
RESOURCE_KINDS
RESOURCE_KINDS_TO_EXTERNAL_NAMES

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

ResourceType

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]
parent[RW]
resource_type_collection[RW]
ruby_code[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 Instance Methods

assign_parameter_values(parameters, resource) click to toggle source
# 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
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, line 100
def child_of?(klass)
  return false unless parent

  return(klass == parent_type ? true : parent_type.child_of?(klass))
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, 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
evaluate_code(resource) click to toggle source

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
instantiate_resource(scope, resource) click to toggle source
# 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
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, line 159
def match(string)
  return string.to_s.downcase == name unless name_is_regex?

  @match = @name.match(string)
end
merge(other) click to toggle source

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
name() click to toggle source
# File lib/puppet/resource/type.rb, line 232
def name
  return @name unless @name.is_a?(Regexp)
  @name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'')
end
name_is_regex?() click to toggle source
# File lib/puppet/resource/type.rb, line 237
def name_is_regex?
  @name.is_a?(Regexp)
end
parent_type(scope = nil) click to toggle source

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
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. 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
set_arguments(arguments) click to toggle source
# 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_resource_parameters(resource, scope) click to toggle source

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
to_data_hash() click to toggle source
# 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
valid_parameter?(param) click to toggle source

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

Public Class Methods

from_data_hash(data) click to toggle source
# 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
from_pson(data) click to toggle source
# 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
new(type, name, options = {}) click to toggle source
# 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