File: runtime3_support.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (535 lines) | stat: -rw-r--r-- 26,393 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
160
161
162
163
164
165
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
module Puppet::Pops
module Evaluator
# A module with bindings between the new evaluator and the 3x runtime.
# The intention is to separate all calls into scope, compiler, resource, etc. in this module
# to make it easier to later refactor the evaluator for better implementations of the 3x classes.
#
# @api private
module Runtime3Support

  NAME_SPACE_SEPARATOR = '::'.freeze

  # Fails the evaluation of _semantic_ with a given issue.
  #
  # @param issue [Issue] the issue to report
  # @param semantic [ModelPopsObject] the object for which evaluation failed in some way. Used to determine origin.
  # @param options [Hash] hash of optional named data elements for the given issue
  # @return [!] this method does not return
  # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError?)
  #
  def fail(issue, semantic, options={}, except=nil)
    optionally_fail(issue, semantic, options, except)
    # an error should have been raised since fail always fails
    raise ArgumentError, _("Internal Error: Configuration of runtime error handling wrong: should have raised exception")
  end

  # Optionally (based on severity) Fails the evaluation of _semantic_ with a given issue
  # If the given issue is configured to be of severity < :error it is only reported, and the function returns.
  #
  # @param issue [Issue] the issue to report
  # @param semantic [ModelPopsObject] the object for which evaluation failed in some way. Used to determine origin.
  # @param options [Hash] hash of optional named data elements for the given issue
  # @return [!] this method does not return
  # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError?)
  #
  def optionally_fail(issue, semantic, options={}, except=nil)
    if except.nil? && diagnostic_producer.severity_producer[issue] == :error
      # Want a stacktrace, and it must be passed as an exception
      begin
       raise EvaluationError.new()
      rescue EvaluationError => e
        except = e
      end
    end
    diagnostic_producer.accept(issue, semantic, options, except)
  end

  # Optionally (based on severity) Fails the evaluation with a given issue
  # If the given issue is configured to be of severity < :error it is only reported, and the function returns.
  # The location the issue is reported against is found is based on the top file/line in the puppet call stack
  #
  # @param issue [Issue] the issue to report
  # @param options [Hash] hash of optional named data elements for the given issue
  # @return [!] this method may not return, nil if it does
  # @raise [Puppet::ParseError] an evaluation error initialized from the arguments
  #
  def runtime_issue(issue, options={})
    # Get position from puppet runtime stack
    file, line = Puppet::Pops::PuppetStack.top_of_stack

    # Use a SemanticError as the sourcepos
    semantic = Puppet::Pops::SemanticError.new(issue, nil, options.merge({:file => file, :line => line}))
    optionally_fail(issue,  semantic)
    nil
  end

  # Binds the given variable name to the given value in the given scope.
  # The reference object `o` is intended to be used for origin information - the 3x scope implementation
  # only makes use of location when there is an error. This is now handled by other mechanisms; first a check
  # is made if a variable exists and an error is raised if attempting to change an immutable value. Errors
  # in name, numeric variable assignment etc. have also been validated prior to this call. In the event the
  # scope.setvar still raises an error, the general exception handling for evaluation of the assignment
  # expression knows about its location. Because of this, there is no need to extract the location for each
  # setting (extraction is somewhat expensive since 3x requires line instead of offset).
  #
  def set_variable(name, value, o, scope)
    # Scope also checks this but requires that location information are passed as options.
    # Those are expensive to calculate and a test is instead made here to enable failing with better information.
    # The error is not specific enough to allow catching it - need to check the actual message text.
    # TODO: Improve the messy implementation in Scope.
    #
    if name == "server_facts"
      fail(Issues::ILLEGAL_RESERVED_ASSIGNMENT, o, {:name => name} )
    end

    if scope.bound?(name)
      if Puppet::Parser::Scope::RESERVED_VARIABLE_NAMES.include?(name)
        fail(Issues::ILLEGAL_RESERVED_ASSIGNMENT, o, {:name => name} )
      else
        fail(Issues::ILLEGAL_REASSIGNMENT, o, {:name => name} )
      end
    end
    scope.setvar(name, value)
  end

  # Returns the value of the variable (nil is returned if variable has no value, or if variable does not exist)
  #
  def get_variable_value(name, o, scope)
    # Puppet 3x stores all variables as strings (then converts them back to numeric with a regexp... to see if it is a match variable)
    # Not ideal, scope should support numeric lookup directly instead.
    # TODO: consider fixing scope
    catch(:undefined_variable) {
      x = scope.lookupvar(name.to_s)
      # Must convert :undef back to nil - this can happen when an undefined variable is used in a
      # parameter's default value expression - there nil must be :undef to work with the rest of 3x.
      # Now that the value comes back to 4x it is changed to nil.
      return :undef.equal?(x) ? nil : x
    }
    # It is always ok to reference numeric variables even if they are not assigned. They are always undef
    # if not set by a match expression.
    #
    unless name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME
      optionally_fail(Puppet::Pops::Issues::UNKNOWN_VARIABLE, o, {:name => name})
    end
    nil # in case unknown variable is configured as a warning or ignore
  end

  # Returns true if the variable of the given name is set in the given most nested scope. True is returned even if
  # variable is bound to nil.
  #
  def variable_bound?(name, scope)
    scope.bound?(name.to_s)
  end

  # Returns true if the variable is bound to a value or nil, in the scope or it's parent scopes.
  #
  def variable_exists?(name, scope)
    scope.exist?(name.to_s)
  end

  def set_match_data(match_data, scope)
    # See set_variable for rationale for not passing file and line to ephemeral_from.
    # NOTE: The 3x scope adds one ephemeral(match) to its internal stack per match that succeeds ! It never
    # clears anything. Thus a context that performs many matches will get very deep (there simply is no way to
    # clear the match variables without rolling back the ephemeral stack.)
    # This implementation does not attempt to fix this, it behaves the same bad way.
    unless match_data.nil?
      scope.ephemeral_from(match_data)
    end
  end

  # Creates a local scope with vairalbes set from a hash of variable name to value
  #
  def create_local_scope_from(hash, scope)
    # two dummy values are needed since the scope tries to give an error message (can not happen in this
    # case - it is just wrong, the error should be reported by the caller who knows in more detail where it
    # is in the source.
    #
    raise ArgumentError, _("Internal error - attempt to create a local scope without a hash") unless hash.is_a?(Hash)
    scope.ephemeral_from(hash)
  end

  # Creates a nested match scope
  def create_match_scope_from(scope)
    # Create a transparent match scope (for future matches)
    scope.new_match_scope(nil)
  end

  def get_scope_nesting_level(scope)
    scope.ephemeral_level
  end

  def set_scope_nesting_level(scope, level)
    # 3x uses this method to reset the level,
    scope.pop_ephemerals(level)
  end

  # Adds a relationship between the given `source` and `target` of the given `relationship_type`
  # @param source [Puppet:Pops::Types::PCatalogEntryType] the source end of the relationship (from)
  # @param target [Puppet:Pops::Types::PCatalogEntryType] the target end of the relationship (to)
  # @param relationship_type [:relationship, :subscription] the type of the relationship
  #
  def add_relationship(source, target, relationship_type, scope)
    # The 3x way is to record a Puppet::Parser::Relationship that is evaluated at the end of the compilation.
    # This means it is not possible to detect any duplicates at this point (and signal where an attempt is made to
    # add a duplicate. There is also no location information to signal the original place in the logic. The user will have
    # to go fish.
    # The 3.x implementation is based on Strings :-o, so the source and target must be transformed. The resolution is
    # done by Catalog#resource(type, title). To do that, it creates a Puppet::Resource since it is responsible for
    # translating the name/type/title and create index-keys used by the catalog. The Puppet::Resource has bizarre parsing of
    # the type and title (scan for [] that is interpreted as type/title (but it gets it wrong).
    # Moreover if the type is "" or "component", the type is Class, and if the type is :main, it is :main, all other cases
    # undergo capitalization of name-segments (foo::bar becomes Foo::Bar). (This was earlier done in the reverse by the parser).
    # Further, the title undergoes the same munging !!!
    #
    # That bug infested nest of messy logic needs serious Exorcism!
    #
    # Unfortunately it is not easy to simply call more intelligent methods at a lower level as the compiler evaluates the recorded
    # Relationship object at a much later point, and it is responsible for invoking all the messy logic.
    #
    # TODO: Revisit the below logic when there is a sane implementation of the catalog, compiler and resource. For now
    # concentrate on transforming the type references to what is expected by the wacky logic.
    #
    # HOWEVER, the Compiler only records the Relationships, and the only method it calls is @relationships.each{|x| x.evaluate(catalog) }
    # Which means a smarter Relationship class could do this right. Instead of obtaining the resource from the catalog using
    # the borked resource(type, title) which creates a resource for the purpose of looking it up, it needs to instead
    # scan the catalog's resources
    #
    # GAAAH, it is even worse!
    # It starts in the parser, which parses "File['foo']" into an AST::ResourceReference with type = File, and title = foo
    # This AST is evaluated by looking up the type/title in the scope - causing it to be loaded if it exists, and if not, the given
    # type name/title is used. It does not search for resource instances, only classes and types. It returns symbolic information
    # [type, [title, title]]. From this, instances of Puppet::Resource are created and returned. These only have type/title information
    # filled out. One or an array of resources are returned.
    # This set of evaluated (empty reference) Resource instances are then passed to the relationship operator. It creates a
    # Puppet::Parser::Relationship giving it a source and a target that are (empty reference) Resource instances. These are then remembered
    # until the relationship is evaluated by the compiler (at the end). When evaluation takes place, the (empty reference) Resource instances
    # are converted to String (!?! WTF) on the simple format "#{type}[#{title}]", and the catalog is told to find a resource, by giving
    # it this string. If it cannot find the resource it fails, else the before/notify parameter is appended with the target.
    # The search for the resource begin with (you guessed it) again creating an (empty reference) resource from type and title (WTF?!?!).
    # The catalog now uses the reference resource to compute a key [r.type, r.title.to_s] and also gets a uniqueness key from the
    # resource (This is only a reference type created from title and type). If it cannot find it with the first key, it uses the
    # uniqueness key to lookup.
    #
    # This is probably done to allow a resource type to munge/translate the title in some way (but it is quite unclear from the long
    # and convoluted path of evaluation.
    # In order to do this in a way that is similar to 3.x two resources are created to be used as keys.
    #
    # And if that is not enough, a source/target may be a Collector (a baked query that will be evaluated by the
    # compiler - it is simply passed through here for processing by the compiler at the right time).
    #
    if source.is_a?(Collectors::AbstractCollector)
      # use verbatim - behavior defined by 3x
      source_resource = source
    else
      # transform into the wonderful String representation in 3x
      type, title = Runtime3Converter.instance.catalog_type_to_split_type_title(source)
      type = Runtime3ResourceSupport.find_resource_type(scope, type) unless type == 'class' || type == 'node'
      source_resource = Puppet::Resource.new(type, title)
    end
    if target.is_a?(Collectors::AbstractCollector)
      # use verbatim - behavior defined by 3x
      target_resource = target
    else
      # transform into the wonderful String representation in 3x
      type, title = Runtime3Converter.instance.catalog_type_to_split_type_title(target)
      type = Runtime3ResourceSupport.find_resource_type(scope, type) unless type == 'class' || type == 'node'
      target_resource = Puppet::Resource.new(type, title)
    end
    # Add the relationship to the compiler for later evaluation.
    scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, relationship_type))
  end

  # Coerce value `v` to numeric or fails.
  # The given value `v` is coerced to Numeric, and if that fails the operation
  # calls {#fail}.
  # @param v [Object] the value to convert
  # @param o [Object] originating instruction
  # @param scope [Object] the (runtime specific) scope where evaluation of o takes place
  # @return [Numeric] value `v` converted to Numeric.
  #
  def coerce_numeric(v, o, scope)
    if v.is_a?(Numeric)
      return v
    end
    n = Utils.to_n(v)
    unless n
      fail(Issues::NOT_NUMERIC, o, {:value => v})
    end
    # this point is reached if there was a conversion
    optionally_fail(Issues::NUMERIC_COERCION, o, {:before => v, :after => n})
    n
  end

  # Provides the ability to call a 3.x or 4.x function from the perspective of a 3.x function or ERB template.
  # The arguments to the function must be an Array containing values compliant with the 4.x calling convention.
  # If the targeted function is a 3.x function, the values will be transformed.
  # @param name [String] the name of the function (without the 'function_' prefix used by scope)
  # @param args [Array] arguments, may be empty
  # @param scope [Object] the (runtime specific) scope where evaluation takes place
  # @raise [ArgumentError] 'unknown function' if the function does not exist
  #
  def external_call_function(name, args, scope, &block)
    # Call via 4x API if the function exists there
    loaders = scope.compiler.loaders
    # Since this is a call from non puppet code, it is not possible to relate it to a module loader
    # It is known where the call originates by using the scope associated module - but this is the calling scope
    # and it does not defined the visibility of functions from a ruby function's perspective. Instead,
    # this is done from the perspective of the environment.
    loader = loaders.private_environment_loader
    func = loader.load(:function, name) if loader
    if func
      Puppet::Util::Profiler.profile(name, [:functions, name]) do
        return func.call(scope, *args, &block)
      end
    end

    # Call via 3x API if function exists there
    raise ArgumentError, _("Unknown function '%{name}'") % { name: name } unless Puppet::Parser::Functions.function(name)

    # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x.
    # NOTE: Passing an empty string last converts nil/:undef to empty string
    mapped_args = Runtime3FunctionArgumentConverter.map_args(args, scope, '')
    result = scope.send("function_#{name}", mapped_args, &block)
    # Prevent non r-value functions from leaking their result (they are not written to care about this)
    Puppet::Parser::Functions.rvalue?(name) ? result : nil
  end

  def call_function(name, args, o, scope, &block)
    file, line = extract_file_line(o)
    loader = Adapters::LoaderAdapter.loader_for_model_object(o, file)
    func = loader.load(:function, name) if loader
    if func
      Puppet::Util::Profiler.profile(name, [:functions, name]) do
        # Add stack frame when calling.
        return Puppet::Pops::PuppetStack.stack(file || '', line, func, :call, [scope, *args], &block)
      end
    end
    # Call via 3x API if function exists there without having been autoloaded
    fail(Issues::UNKNOWN_FUNCTION, o, {:name => name}) unless Puppet::Parser::Functions.function(name)

    # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x.
    # NOTE: Passing an empty string last converts nil/:undef to empty string
    mapped_args = Runtime3FunctionArgumentConverter.map_args(args, scope, '')
    # The 3x function performs return value mapping and returns nil if it is not of rvalue type
    Puppet::Pops::PuppetStack.stack(file, line, scope, "function_#{name}", [mapped_args], &block)
  end

  # The o is used for source reference
  def create_resource_parameter(o, scope, name, value, operator)
    file, line = extract_file_line(o)
    Puppet::Parser::Resource::Param.new(
      :name   => name,
      :value  => convert(value, scope, nil), # converted to 3x since 4x supports additional objects / types
      :source => scope.source, :line => line, :file => file,
      :add    => operator == '+>'
    )
  end

  def convert(value, scope, undef_value)
    Runtime3Converter.instance.convert(value, scope, undef_value)
  end

  def create_resources(o, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)
    # Not 100% accurate as this is the resource expression location and each title is processed separately
    # The titles are however the result of evaluation and they have no location at this point (an array
    # of positions for the source expressions are required for this to work).
    # TODO: Revisit and possible improve the accuracy.
    #
    file, line = extract_file_line(o)
    Runtime3ResourceSupport.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)
  end

  # Defines default parameters for a type with the given name.
  #
  def create_resource_defaults(o, scope, type_name, evaluated_parameters)
    # Note that name must be capitalized in this 3x call
    # The 3x impl creates a Resource instance with a bogus title and then asks the created resource
    # for the type of the name.
    # Note, locations are available per parameter.
    #
    scope.define_settings(capitalize_qualified_name(type_name), evaluated_parameters.flatten)
  end

  # Capitalizes each segment of a qualified name
  #
  def capitalize_qualified_name(name)
    name.split(/::/).map(&:capitalize).join(NAME_SPACE_SEPARATOR)
  end

  # Creates resource overrides for all resource type objects in evaluated_resources. The same set of
  # evaluated parameters are applied to all.
  #
  def create_resource_overrides(o, scope, evaluated_resources, evaluated_parameters)
    # Not 100% accurate as this is the resource expression location and each title is processed separately
    # The titles are however the result of evaluation and they have no location at this point (an array
    # of positions for the source expressions are required for this to work.
    # TODO: Revisit and possible improve the accuracy.
    #
    file, line = extract_file_line(o)
    # A *=> results in an array of arrays
    evaluated_parameters = evaluated_parameters.flatten
    evaluated_resources.each do |r|
      unless r.is_a?(Types::PResourceType) && r.type_name != 'class'
        fail(Issues::ILLEGAL_OVERRIDDEN_TYPE, o, {:actual => r} )
      end
      t = Runtime3ResourceSupport.find_resource_type(scope, r.type_name)
      resource = Puppet::Parser::Resource.new(
        t, r.title, {
          :parameters => evaluated_parameters,
          :file => file,
          :line => line,
          # WTF is this? Which source is this? The file? The name of the context ?
          :source => scope.source,
          :scope => scope
        }, false # defaults should not override
      )

      scope.compiler.add_override(resource)
    end
  end

  # Finds a resource given a type and a title.
  #
  def find_resource(scope, type_name, title)
    scope.compiler.findresource(type_name, title)
  end

  # Returns the value of a resource's parameter by first looking up the parameter in the resource
  # and then in the defaults for the resource. Since the resource exists (it must in order to look up its
  # parameters, any overrides have already been applied). Defaults are not applied to a resource until it
  # has been finished (which typically has not taken place when this is evaluated; hence the dual lookup).
  #
  def get_resource_parameter_value(scope, resource, parameter_name)
    # This gets the parameter value, or nil (for both valid parameters and parameters that do not exist).
    val = resource[parameter_name]

    # Sometimes the resource is a Puppet::Parser::Resource and sometimes it is
    # a Puppet::Resource. The Puppet::Resource case occurs when puppet language
    # is evaluated against an already completed catalog (where all instances of
    # Puppet::Parser::Resource are converted to Puppet::Resource instances).
    # Evaluating against an already completed catalog is really only found in
    # the language specification tests, where the puppet language is used to
    # test itself.
    if resource.is_a?(Puppet::Parser::Resource)
      # The defaults must be looked up in the scope where the resource was created (not in the given
      # scope where the lookup takes place.
      resource_scope = resource.scope
      defaults = resource_scope.lookupdefaults(resource.type) if val.nil? && resource_scope
      if defaults
        # NOTE: 3x resource keeps defaults as hash using symbol for name as key to Parameter which (again) holds
        # name and value.
        # NOTE: meta parameters that are unset ends up here, and there are no defaults for those encoded
        # in the defaults, they may receive hardcoded defaults later (e.g. 'tag').
        param = defaults[parameter_name.to_sym]
        # Some parameters (meta parameters like 'tag') does not return a param from which the value can be obtained
        # at all times. Instead, they return a nil param until a value has been set.
        val = param.nil? ? nil : param.value
      end
    end
    val
  end

  # Returns true, if the given name is the name of a resource parameter.
  #
  def is_parameter_of_resource?(scope, resource, name)
    return false unless name.is_a?(String)
    resource.valid_parameter?(name)
  end

  def resource_to_ptype(resource)
    nil if resource.nil?
    # inference returns the meta type since the 3x Resource is an alternate way to describe a type
    type_calculator.infer(resource).type
  end

  # This is the same type of "truth" as used in the current Puppet DSL.
  #
  def is_true?(value, o)
    # Is the value true?  This allows us to control the definition of truth
    # in one place.
    case value
    # Support :undef since it may come from a 3x structure
    when :undef
      false
    when String
      true
    else
      !!value
    end
  end

  # Utility method for TrueClass || FalseClass
  # @param x [Object] the object to test if it is instance of TrueClass or FalseClass
  def is_boolean? x
    x.is_a?(TrueClass) || x.is_a?(FalseClass)
  end

  def extract_file_line(o)
    o.is_a?(Model::Positioned) ? [o.file, o.line] : [nil, -1]
  end

  # Creates a diagnostic producer
  def diagnostic_producer
    Validation::DiagnosticProducer.new(
      ExceptionRaisingAcceptor.new(),                   # Raises exception on all issues
      SeverityProducer.new(), # All issues are errors
      Model::ModelLabelProvider.new())
  end

  # Configure the severity of failures
  class SeverityProducer < Validation::SeverityProducer
    Issues = Issues

    def initialize
      super
      p = self
      # Issues triggering warning only if --debug is on
      if Puppet[:debug]
        p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :warning
      else
        p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :ignore
      end

      # if strict variables are on, an error is raised
      # if strict variables are off, the Puppet[strict] defines what is done
      #
      if Puppet[:strict_variables]
        p[Issues::UNKNOWN_VARIABLE] = :error
      elsif Puppet[:strict] == :off
        p[Issues::UNKNOWN_VARIABLE] = :ignore
      else
        p[Issues::UNKNOWN_VARIABLE] = Puppet[:strict]
      end

      # Store config issues, ignore or warning
      p[Issues::RT_NO_STORECONFIGS_EXPORT]    = Puppet[:storeconfigs] ? :ignore : :warning
      p[Issues::RT_NO_STORECONFIGS]           = Puppet[:storeconfigs] ? :ignore : :warning
      p[Issues::CLASS_NOT_VIRTUALIZABLE]      = :error
      p[Issues::NUMERIC_COERCION]             = Puppet[:strict] == :off ? :ignore : Puppet[:strict]
      p[Issues::SERIALIZATION_DEFAULT_CONVERTED_TO_STRING] = Puppet[:strict] == :off ? :warning : Puppet[:strict]
      p[Issues::SERIALIZATION_UNKNOWN_CONVERTED_TO_STRING] = Puppet[:strict] == :off ? :warning : Puppet[:strict]
      p[Issues::SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING] = Puppet[:strict] == :off ? :warning : Puppet[:strict]
    end
  end

  # An acceptor of diagnostics that immediately raises an exception.
  class ExceptionRaisingAcceptor < Validation::Acceptor
    def accept(diagnostic)
      super
      IssueReporter.assert_and_report(self, {
        :message => "Evaluation Error:",
        :emit_warnings => true,  # log warnings
        :exception_class => Puppet::PreformattedError
      })
      if errors?
        raise ArgumentError, _("Internal Error: Configuration of runtime error handling wrong: should have raised exception")
      end
    end
  end

  class EvaluationError < StandardError
  end
end
end
end