Class: Puppet::Resource::Type

Inherits:
Object
  • Object
show all
Includes:
Util::Errors, Util::Warnings
Defined in:
lib/puppet/resource/type.rb

Overview

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

Constant Summary collapse

RESOURCE_KINDS =
[:hostclass, :node, :definition]
RESOURCE_KINDS_TO_EXTERNAL_NAMES =

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

{
  :hostclass => "class",
  :node => "node",
  :definition => "defined_type"
}
RESOURCE_EXTERNAL_NAMES_TO_KINDS =
RESOURCE_KINDS_TO_EXTERNAL_NAMES.invert
NAME =
'name'
TITLE =
'title'
MODULE_NAME =
'module_name'
CALLER_MODULE_NAME =
'caller_module_name'
PARAMETERS =
'parameters'
KIND =
'kind'
NODES =
'nodes'
DOUBLE_COLON =
'::'
EMPTY_ARRAY =
[].freeze
LOOKAROUND_OPERATORS =
{
  "(" => 'LP',
  "?" => "QU",
  "<" => "LT",
  ">" => "GT",
  "!" => "EX",
  "=" => "EQ",
  ")" => 'RP'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util::Errors

#adderrorcontext, #devfail, #error_context, error_location, error_location_with_space, error_location_with_unknowns, #exceptwrap, #fail

Methods included from Util::Warnings

clear_warnings, debug_once, maybe_log, notice_once, warnonce

Constructor Details

#initialize(type, name, options = {}) ⇒ Type

Returns a new instance of Type.

Raises:

  • (ArgumentError)


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/puppet/resource/type.rb', line 96

def initialize(type, name, options = {})
  @type = type.to_s.downcase.to_sym
  raise ArgumentError, _("Invalid resource supertype '%{type}'") % { type: 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|
    value = options[param]
    next unless value

    send(param.to_s + '=', value)
  end

  set_arguments(options[:arguments])
  set_argument_types(options[:argument_types])

  @match = nil

  @module_name = options[:module_name]
end

Instance Attribute Details

#argument_typesHash<Symbol, Puppet::Pops::Types::PAnyType] map from name to type (readonly)

Map from argument (aka parameter) names to Puppet Type

Returns:



52
53
54
# File 'lib/puppet/resource/type.rb', line 52

def argument_types
  @argument_types
end

#argumentsObject (readonly)



47
48
49
# File 'lib/puppet/resource/type.rb', line 47

def arguments
  @arguments
end

#behaves_likeObject (readonly)



47
48
49
# File 'lib/puppet/resource/type.rb', line 47

def behaves_like
  @behaves_like
end

#codeObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def code
  @code
end

#docObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def doc
  @doc
end

#fileObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def file
  @file
end

#lineObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def line
  @line
end

#module_nameObject (readonly)



47
48
49
# File 'lib/puppet/resource/type.rb', line 47

def module_name
  @module_name
end

#namespaceObject (readonly)



47
48
49
# File 'lib/puppet/resource/type.rb', line 47

def namespace
  @namespace
end

#overrideObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def override
  @override
end

#parentObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def parent
  @parent
end

#resource_type_collectionObject



46
47
48
# File 'lib/puppet/resource/type.rb', line 46

def resource_type_collection
  @resource_type_collection
end

#typeObject (readonly)

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


57
58
59
# File 'lib/puppet/resource/type.rb', line 57

def type
  @type
end

Instance Method Details

#child_of?(klass) ⇒ Boolean

Are we a child of the passed class? Do a recursive search up our parentage tree to figure it out.

Returns:

  • (Boolean)


65
66
67
68
69
70
# File 'lib/puppet/resource/type.rb', line 65

def child_of?(klass)
  return true if override
  return false unless parent

  (klass == parent_type ? true : parent_type.child_of?(klass))
end

#ensure_in_catalog(scope, parameters = nil) ⇒ Object

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.



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/puppet/resource/type.rb', line 166

def ensure_in_catalog(scope, parameters = nil)
  resource_type =
    case type
    when :definition
      raise ArgumentError, _('Cannot create resources for defined resource types')
    when :hostclass
      :class
    when :node
      :node
    end

  # 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 parameters.nil?
    resource = scope.catalog.resource(resource_type, name)
    return resource unless resource.nil?
  elsif parameters.is_a?(Hash)
    parameters = parameters.map { |k, v| Puppet::Parser::Resource::Param.new(:name => k, :value => v, :source => self) }
  end
  resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self, :parameters => parameters)
  instantiate_resource(scope, resource)
  scope.compiler.add_resource(scope, resource)
  resource
end

#evaluate_code(resource) ⇒ Object

Now evaluate the code associated with this class or definition.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/puppet/resource/type.rb', line 73

def evaluate_code(resource)
  static_parent = evaluate_parent_type(resource)
  scope = static_parent || resource.scope

  scope = scope.newscope(: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
      scope.with_guarded_scope do
        scope.ephemeral_from(@match, file, line)
        code.safeevaluate(scope)
      end
    else
      code.safeevaluate(scope)
    end
  end
end

#instantiate_resource(scope, resource) ⇒ Object



196
197
198
199
200
201
202
203
204
205
# File 'lib/puppet/resource/type.rb', line 196

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 %w[Class Node].include? resource.type
    scope.catalog.merge_tags_from(resource)
  end
end

#match(string) ⇒ Object

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



121
122
123
124
125
# File 'lib/puppet/resource/type.rb', line 121

def match(string)
  return string.to_s.downcase == name unless name_is_regex?

  @match = @name.match(string)
end

#merge(other) ⇒ Object

Add code from a new instance to our code.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/puppet/resource/type.rb', line 128

def merge(other)
  fail _("%{name} is not a class; cannot add code to it") % { name: name } unless type == :hostclass
  fail _("%{name} is not a class; cannot add code from it") % { name: other.name } unless other.type == :hostclass

  if name == "" && Puppet.settings[:freeze_main]
    # It is ok to merge definitions into main even if freeze is on (definitions are nodes, classes, defines, functions, and types)
    unless other.code.is_definitions_only?
      fail _("Cannot have code outside of a class/node/define because 'freeze_main' is enabled")
    end
  end
  if parent and other.parent and parent != other.parent
    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 }
  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 code
    self.code = other.code
    return
  end

  self.code = Puppet::Parser::ParserFactory.code_merger.concatenate([self, other])
end

#nameObject



207
208
209
210
211
212
213
214
215
216
217
# File 'lib/puppet/resource/type.rb', line 207

def name
  if type == :node && name_is_regex?
    # Normalize lookarround regex patthern
    internal_name = @name.source.downcase.gsub(/\(\?[^)]*\)/) do |str|
      str.gsub(/./) { |ch| LOOKAROUND_OPERATORS[ch] || ch }
    end
    "__node_regexp__#{internal_name.gsub(/[^-\w:.]/, '').sub(/^\.+/, '')}"
  else
    @name
  end
end

#name_is_regex?Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/puppet/resource/type.rb', line 219

def name_is_regex?
  @name.is_a?(Regexp)
end

#parent_type(scope = nil) ⇒ Object



223
224
225
226
227
228
# File 'lib/puppet/resource/type.rb', line 223

def parent_type(scope = nil)
  return nil unless parent

  @parent_type ||= scope.environment.known_resource_types.send("find_#{type}", parent) ||
                   fail(Puppet::ParseError, _("Could not find parent resource type '%{parent}' of type %{parent_type} in %{env}") % { parent: parent, parent_type: type, env: scope.environment })
end

#set_argument_types(name_to_type_hash) ⇒ Object

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.



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/puppet/resource/type.rb', line 350

def set_argument_types(name_to_type_hash)
  @argument_types = {}
  @parameter_struct = nil
  return unless 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.") % { name: name }
    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 %{class_name}") % { name: name, class_name: t.class }
    end

    @argument_types[name] = t
  end
end

#set_arguments(arguments) ⇒ Object



334
335
336
337
338
339
340
341
342
343
344
# File 'lib/puppet/resource/type.rb', line 334

def set_arguments(arguments)
  @arguments = {}
  @parameter_struct = nil
  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) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Parameters:



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/puppet/resource/type.rb', line 238

def set_resource_parameters(resource, scope)
  # Inject parameters from using external lookup
  modname = resource[:module_name] || module_name
  scope[MODULE_NAME] = modname unless modname.nil?
  caller_name = resource[:caller_module_name] || scope.parent_module_name
  scope[CALLER_MODULE_NAME] = caller_name unless caller_name.nil?

  inject_external_parameters(resource, scope)

  if @type == :hostclass
    scope[TITLE] = resource.title.to_s.downcase
    scope[NAME] = resource.name.to_s.downcase
  else
    scope[TITLE] = resource.title
    scope[NAME] = resource.name
  end
  scope.class_set(name, scope) if hostclass? || node?

  param_hash = scope.with_parameter_scope(resource.to_s, arguments.keys) do |param_scope|
    # Assign directly to the parameter scope to avoid scope parameter validation at this point. It
    # will happen anyway when the values are assigned to the scope after the parameter scoped has
    # been popped.
    resource.each { |k, v| param_scope[k.to_s] = v.value unless k == :name || k == :title }
    assign_defaults(resource, param_scope, scope)
    param_scope.to_hash
  end

  validate_resource_hash(resource, param_hash)

  # Assign parameter values to current scope
  param_hash.each { |param, value| exceptwrap { scope[param] = value } }
end

#valid_parameter?(param) ⇒ Boolean

Check whether a given argument is valid.

Returns:

  • (Boolean)


330
331
332
# File 'lib/puppet/resource/type.rb', line 330

def valid_parameter?(param)
  parameter_struct.hashed_elements.include?(param.to_s)
end

#validate_resource(resource) ⇒ Object

Validate that all parameters given to the resource are correct

Parameters:



318
319
320
321
322
323
324
325
326
327
# File 'lib/puppet/resource/type.rb', line 318

def validate_resource(resource)
  # Since Sensitive values have special encoding (in a separate parameter) an unwrapped sensitive value must be
  # recreated as a Sensitive in order to perform correct type checking.
  sensitives = Set.new(resource.sensitive_parameters)
  validate_resource_hash(resource,
                         resource.parameters.to_h do |name, value|
                           value_to_validate = sensitives.include?(name) ? Puppet::Pops::Types::PSensitiveType::Sensitive.new(value.value) : value.value
                           [name.to_s, value_to_validate]
                         end)
end