Module: Puppet::ResourceApi::DataTypeHandling

Defined in:
lib/puppet/resource_api/data_type_handling.rb

Overview

This module is used to handle data inside types, contains methods for munging and validation of the type values.

Class Method Summary collapse

Class Method Details

.ambiguous_error_msg(error_msg_prefix, value, type) ⇒ Object

Returns ambiguous error message based on ‘error_msg_prefix`, `value` and `type`.

Parameters:

  • type (Puppet::Pops::Types::TypedModelObject)

    the type to check against

  • value

    the value to clean

  • error_msg_prefix (String)

    a prefix for the error messages



145
146
147
148
# File 'lib/puppet/resource_api/data_type_handling.rb', line 145

def self.ambiguous_error_msg(error_msg_prefix, value, type)
  "#{error_msg_prefix} #{value.inspect} is not unabiguously convertable to " \
  "#{type}"
end

.boolean_munge(value) ⇒ Object

Returns correct boolean ‘value` based on one specified in `type`.

Parameters:

  • value

    the value to boolean munge



128
129
130
131
132
133
134
135
136
137
# File 'lib/puppet/resource_api/data_type_handling.rb', line 128

def self.boolean_munge(value)
  case value
  when 'true', :true # rubocop:disable Lint/BooleanSymbol
    true
  when 'false', :false # rubocop:disable Lint/BooleanSymbol
    false
  else
    value
  end
end

.mungify(type, value, error_msg_prefix, unpack_strings = false) ⇒ type

This method handles translating values from the runtime environment to the expected types for the provider with validation. When being called from ‘puppet resource`, it tries to transform the strings from the command line into their expected ruby representations, e.g. `“2”` (a string), will be transformed to `2` (the number) if (and only if) the target `type` is `Integer`. Additionally this function also validates that the passed in (and optionally transformed) value matches the specified type. against. legacy type

Parameters:

  • type (Puppet::Pops::Types::TypedModelObject)

    the type to check/clean

  • value

    the value to clean

  • error_msg_prefix (String)

    a prefix for the error messages

  • unpack_strings (Boolean) (defaults to: false)

    unpacking of strings for migrating off

Returns:

  • (type)

    the cleaned value



21
22
23
24
25
26
27
28
29
30
# File 'lib/puppet/resource_api/data_type_handling.rb', line 21

def self.mungify(type, value, error_msg_prefix, unpack_strings = false)
  cleaned_value = mungify_core(
    type,
    value,
    error_msg_prefix,
    unpack_strings,
  )
  validate(type, cleaned_value, error_msg_prefix)
  cleaned_value
end

.mungify_core(type, value, error_msg_prefix, unpack_strings = false) ⇒ type

This is core method used in mungify which handles translating values to expected cleaned type values, result is not validated. against. legacy type

Parameters:

  • type (Puppet::Pops::Types::TypedModelObject)

    the type to check/clean

  • value

    the value to clean

  • error_msg_prefix (String)

    a prefix for the error messages

  • unpack_strings (Boolean) (defaults to: false)

    unpacking of strings for migrating off

Returns:

  • (type)

    the cleaned value

Raises:

  • (Puppet::ResourceError)

    if ‘value` could not be parsed into `type`



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/puppet/resource_api/data_type_handling.rb', line 43

def self.mungify_core(type, value, error_msg_prefix, unpack_strings = false)
  if unpack_strings
    # When the provider is exercised from the `puppet resource` CLI, we need
    # to unpack strings into the correct types, e.g. "1" (a string)
    # to 1 (an integer)
    cleaned_value, error_msg = try_mungify(type, value, error_msg_prefix)
    raise Puppet::ResourceError, error_msg if error_msg
    cleaned_value
  elsif value == :false # rubocop:disable Lint/BooleanSymbol
    # work around https://tickets.puppetlabs.com/browse/PUP-2368
    false
  elsif value == :true # rubocop:disable Lint/BooleanSymbol
    # work around https://tickets.puppetlabs.com/browse/PUP-2368
    true
  else
    # Every other time, we can use the values as is
    value
  end
end

.parse_puppet_type(attr_name, type) ⇒ Object



189
190
191
192
193
194
195
196
197
# File 'lib/puppet/resource_api/data_type_handling.rb', line 189

def self.parse_puppet_type(attr_name, type)
  Puppet::Pops::Types::TypeParser.singleton.parse(type)
rescue Puppet::ParseErrorWithIssue => e
  raise Puppet::DevError, "The type of the `#{attr_name}` attribute " \
        "`#{type}` could not be parsed: #{e.message}"
rescue Puppet::ParseError => e
  raise Puppet::DevError, "The type of the `#{attr_name}` attribute " \
        "`#{type}` is not recognised: #{e.message}"
end

.try_mungify(type, value, error_msg_prefix) ⇒ Array

Recursive implementation part of #mungify_core. Uses a multi-valued return value to avoid excessive exception throwing for regular usage.

Returns:

  • (Array)

    if the mungify worked, the first element is the cleaned value, and the second element is nil. If the mungify failed, the first element is nil, and the second element is an error message



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
# File 'lib/puppet/resource_api/data_type_handling.rb', line 69

def self.try_mungify(type, value, error_msg_prefix)
  case type
  when Puppet::Pops::Types::PArrayType
    if value.is_a? Array
      conversions = value.map do |v|
        try_mungify(type.element_type, v, error_msg_prefix)
      end
      # only convert the values if none failed. otherwise fall through and
      # rely on puppet to render a proper error
      if conversions.all? { |c| c[1].nil? }
        value = conversions.map { |c| c[0] }
      end
    end
  when Puppet::Pops::Types::PBooleanType
    value = boolean_munge(value)
  when Puppet::Pops::Types::PIntegerType,
       Puppet::Pops::Types::PFloatType,
       Puppet::Pops::Types::PNumericType
    if value =~ %r{^-?\d+$} || value =~ Puppet::Pops::Patterns::NUMERIC
      value = Puppet::Pops::Utils.to_n(value)
    end
  when Puppet::Pops::Types::PEnumType,
       Puppet::Pops::Types::PStringType,
       Puppet::Pops::Types::PPatternType
    value = value.to_s if value.is_a? Symbol
  when Puppet::Pops::Types::POptionalType
    return value.nil? ? [nil, nil] : try_mungify(type.type, value, error_msg_prefix)
  when Puppet::Pops::Types::PVariantType
    # try converting to anything except string first
    string_type = type.types.find { |t| t.is_a? Puppet::Pops::Types::PStringType }
    conversion_results = (type.types - [string_type]).map do |t|
      try_mungify(t, value, error_msg_prefix)
    end

    # only consider valid results
    conversion_results = conversion_results.select { |r| r[1].nil? }.to_a

    # use the conversion result if unambiguous
    return conversion_results[0] if conversion_results.length == 1

    # return an error if ambiguous
    if conversion_results.length > 1
      return [nil, ambiguous_error_msg(error_msg_prefix, value, type)]
    end

    # try to interpret as string
    return try_mungify(string_type, value, error_msg_prefix) if string_type

    # fall through to default handling
  end

  error_msg = try_validate(type, value, error_msg_prefix)
  return [nil, error_msg] if error_msg # an error
  [value, nil]                         # match
end

.try_validate(type, value, error_msg_prefix) ⇒ String?

Tries to validate the ‘value` against the specified `type`.

Parameters:

  • type (Puppet::Pops::Types::TypedModelObject)

    the type to check against

  • value

    the value to clean

  • error_msg_prefix (String)

    a prefix for the error messages

Returns:

  • (String, nil)

    a error message indicating the problem, or ‘nil` if the value was valid.



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/puppet/resource_api/data_type_handling.rb', line 167

def self.try_validate(type, value, error_msg_prefix)
  return nil if type.instance?(value)

  # an error :-(
  inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value)
  error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch(
    error_msg_prefix,
    type,
    inferred_type,
  )
  error_msg
end

.validate(type, value, error_msg_prefix) ⇒ Object

Validates the ‘value` against the specified `type`.

Parameters:

  • type (Puppet::Pops::Types::TypedModelObject)

    the type to check against

  • value

    the value to clean

  • error_msg_prefix (String)

    a prefix for the error messages

Raises:

  • (Puppet::ResourceError)

    if ‘value` is not of type `type`



156
157
158
159
# File 'lib/puppet/resource_api/data_type_handling.rb', line 156

def self.validate(type, value, error_msg_prefix)
  error_msg = try_validate(type, value, error_msg_prefix)
  raise Puppet::ResourceError, error_msg if error_msg
end

.validate_ensure(definition) ⇒ Object

Raises:

  • (Puppet::DevError)


180
181
182
183
184
185
186
187
# File 'lib/puppet/resource_api/data_type_handling.rb', line 180

def self.validate_ensure(definition)
  return unless definition[:attributes].key? :ensure
  options = definition[:attributes][:ensure]
  type = parse_puppet_type(:ensure, options[:type])

  return if type.is_a?(Puppet::Pops::Types::PEnumType) && type.values.sort == %w[absent present].sort
  raise Puppet::DevError, '`:ensure` attribute must have a type of: `Enum[present, absent]`'
end