Puppet Function: simplib::validate_deep_hash

Defined in:
lib/puppet/functions/simplib/validate_deep_hash.rb
Function type:
Ruby 4.x API

Overview

simplib::validate_deep_hash(Hash $reference, Hash $to_check)Nil

Perform a deep validation on two passed ‘Hashes`.

  • All keys must be defined in the reference ‘Hash` that is being validated against.

  • Unknown keys in the ‘Hash` being compared will cause a failure in validation

  • All values in the final leaves of the ‘reference ’Hash’ must be a String, Boolean, or nil.

  • All values in the final leaves of the ‘Hash` being compared must support a to_s() method.

  • Terminates catalog compilation if validation fails.

Examples:

Passing Examples

reference = {
  'foo' => {
    'bar' => {
      #NOTE: Use quotes for regular expressions instead of '/'
      'baz' => '^\d+$',
      'abc' => '^\w+$',
      'def' => nil
    },
    'baz' => {
      'qrs' => false
      'xyz' => '^true|false$'
    }
  }
}

to_check = {
  'foo' => {
    'bar' => {
      'baz' => ['123', 45]
      'abc' => [ 'these', 'are', 'words' ],
      'def' => 'Anything will work here!'
    },
    'baz' => {
      'qrs' => false
      'xyz' => true
    }
  }
}

validate_deep_hash(reference, to_check)

Failing Examples

reference => { 'foo' => '^\d+$' }
to_check  => { 'foo' => 'abc' }

validate_deep_hash(reference, to_check)

Parameters:

  • reference (Hash)

    Hash to validate against. Keys at all levels of the hash define the structure of the hash and the value at each final leaf in the hash tree contains a regular expression string, a boolean or nil for value validation:

    • When the validation value is a regular expression string, the string representation of the to_check value (from the to_s() method) will be compared to the regular expression contained in the reference string.

    • When the validation value is a Boolean, the string representation of the to_check value will be compared with the string representation of the Boolean (as provided by the to_s() method).

    • When the validation value is a ‘nil` or ’nil’, no value validation will be done for the key.

    • When the to_check value contains an ‘Array` of values for a key, the validation for that key will be applied to each element in that array.

  • to_check (Hash)

    Hash to be validated against the reference

Returns:

  • (Nil)

Raises:

  • (RuntimeError)

    if validation fails



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
# File 'lib/puppet/functions/simplib/validate_deep_hash.rb', line 13

Puppet::Functions.create_function(:'simplib::validate_deep_hash') do

  # @param reference Hash to validate against. Keys at all levels of
  #   the hash define the structure of the hash and the value at each
  #   final leaf in the hash tree contains a regular expression string,
  #   a boolean or nil for value validation:
  #
  #   * When the validation value is a regular expression string,
  #     the string representation of the to_check value (from the
  #     to_s() method) will be compared to the regular expression
  #     contained in the reference string.
  #
  #   * When the validation value is a Boolean, the string
  #     representation of the to_check value will be compared
  #     with the string representation of the Boolean (as provided
  #     by the to_s() method).
  #
  #   * When the validation value is a `nil` or 'nil', no value
  #     validation will be done for the key.
  #
  #   * When the to_check value contains an `Array` of values for a
  #     key, the validation for that key will be applied to each
  #     element in that array.
  #
  # @param to_check Hash to be validated against the reference
  # @return [Nil]
  # @raise [RuntimeError] if validation fails
  #
  # @example Passing Examples
  #   reference = {
  #     'foo' => {
  #       'bar' => {
  #         #NOTE: Use quotes for regular expressions instead of '/'
  #         'baz' => '^\d+$',
  #         'abc' => '^\w+$',
  #         'def' => nil
  #       },
  #       'baz' => {
  #         'qrs' => false
  #         'xyz' => '^true|false$'
  #       }
  #     }
  #   }
  #
  #   to_check = {
  #     'foo' => {
  #       'bar' => {
  #         'baz' => ['123', 45]
  #         'abc' => [ 'these', 'are', 'words' ],
  #         'def' => 'Anything will work here!'
  #       },
  #       'baz' => {
  #         'qrs' => false
  #         'xyz' => true
  #       }
  #     }
  #   }
  #
  #   validate_deep_hash(reference, to_check)
  #
  # @example Failing Examples
  #   reference => { 'foo' => '^\d+$' }
  #   to_check  => { 'foo' => 'abc' }
  #
  #   validate_deep_hash(reference, to_check)
  #
  dispatch :validate_deep_hash do
    required_param 'Hash', :reference
    required_param 'Hash', :to_check
  end

  def validate_deep_hash(reference, to_check)
    invalid = deep_validate(reference, to_check)

    if invalid.size > 0 then
      err_msg = "simplib::validate_deep_hash failed validation:\n  "
      err_msg += invalid.join("\n  ")
      fail(err_msg)
    end
  end

  def valid_value(value)
    [String, TrueClass, FalseClass, Numeric, NilClass].each do |allowed_class|
      return true if value.is_a?(allowed_class)
    end
    return false
  end

  def compare(ref_value, to_check_value)
    return :invalid_ref_type unless valid_value(ref_value)

    ref_string = ref_value.to_s

    Array(to_check_value).each do |value|
      return :invalid_check_type unless value.respond_to?(:to_s)
      return :failed_check unless Regexp.new(ref_string).match(value.to_s)
    end
    return :success
  end

  def deep_validate(reference, to_check, level="TOP", invalid = Array.new)
    to_check.each do |key,value|
      if reference.has_key?(key)
        # skip over keys for which further validation has been disabled
        next if reference[key].nil? or reference[key] == 'nil'

        # Step down a level if value is another hash
        if value.is_a?(Hash)
          if reference[key].is_a?(Hash)
            ref_key_hash = reference[key]
            deep_validate(ref_key_hash, value, level+"-->#{key}", invalid)
          else
            invalid << level + "-->#{key} should not be a Hash"
          end
        # Compare regular expressions since we are at the bottom level
        # (leaf in the Hash tree)
        else
          result = compare(reference[key], to_check[key])
          case result
          when :invalid_ref_type
            err_msg = "simplib::validate_deep_hash(): Check for " +
              level + "-->#{key} has invalid type '#{reference[key].class}'"
            raise ArgumentError.new(err_msg)

          when :invalid_check_type
            invalid << level + "-->#{key} #{to_check[key].class} cannot" +
              " be converted to string for comparison"

          when :failed_check
            invalid << level + "-->#{key} '#{to_check[key]}' must" +
              " validate against '/#{reference[key]}/'"
          end
        end
      else
        invalid << (level+"-->#{key} not in reference hash")
      end
    end
    return invalid
  end
end