puppet 4.9.4 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +6 -0
  3. data/ext/project_data.yaml +2 -2
  4. data/lib/hiera/puppet_function.rb +1 -1
  5. data/lib/puppet.rb +1 -0
  6. data/lib/puppet/application.rb +14 -0
  7. data/lib/puppet/application/inspect.rb +3 -0
  8. data/lib/puppet/defaults.rb +12 -2
  9. data/lib/puppet/etc.rb +125 -0
  10. data/lib/puppet/face/help.rb +1 -1
  11. data/lib/puppet/functions.rb +49 -4
  12. data/lib/puppet/functions/eyaml_lookup_key.rb +12 -3
  13. data/lib/puppet/functions/hocon_data.rb +9 -0
  14. data/lib/puppet/functions/json_data.rb +9 -0
  15. data/lib/puppet/functions/yaml_data.rb +9 -0
  16. data/lib/puppet/indirector/file_bucket_file/file.rb +69 -22
  17. data/lib/puppet/indirector/key/file.rb +2 -1
  18. data/lib/puppet/indirector/ssl_file.rb +24 -3
  19. data/lib/puppet/module.rb +28 -22
  20. data/lib/puppet/network/http/compression.rb +2 -1
  21. data/lib/puppet/parser/compiler.rb +15 -38
  22. data/lib/puppet/parser/functions/hiera.rb +1 -1
  23. data/lib/puppet/parser/functions/hiera_array.rb +1 -1
  24. data/lib/puppet/parser/functions/hiera_hash.rb +1 -1
  25. data/lib/puppet/parser/functions/hiera_include.rb +1 -1
  26. data/lib/puppet/parser/scope.rb +59 -17
  27. data/lib/puppet/pops/evaluator/callable_signature.rb +7 -0
  28. data/lib/puppet/pops/functions/dispatch.rb +18 -5
  29. data/lib/puppet/pops/functions/dispatcher.rb +7 -13
  30. data/lib/puppet/pops/issue_reporter.rb +1 -1
  31. data/lib/puppet/pops/issues.rb +84 -0
  32. data/lib/puppet/pops/loader/base_loader.rb +13 -5
  33. data/lib/puppet/pops/lookup/configured_data_provider.rb +8 -2
  34. data/lib/puppet/pops/lookup/data_dig_function_provider.rb +109 -19
  35. data/lib/puppet/pops/lookup/data_hash_function_provider.rb +19 -4
  36. data/lib/puppet/pops/lookup/data_provider.rb +43 -29
  37. data/lib/puppet/pops/lookup/environment_data_provider.rb +1 -1
  38. data/lib/puppet/pops/lookup/explainer.rb +1 -0
  39. data/lib/puppet/pops/lookup/function_provider.rb +36 -11
  40. data/lib/puppet/pops/lookup/global_data_provider.rb +18 -5
  41. data/lib/puppet/pops/lookup/hiera_config.rb +203 -84
  42. data/lib/puppet/pops/lookup/interpolation.rb +21 -6
  43. data/lib/puppet/pops/lookup/invocation.rb +14 -9
  44. data/lib/puppet/pops/lookup/location_resolver.rb +27 -0
  45. data/lib/puppet/pops/lookup/lookup_adapter.rb +59 -6
  46. data/lib/puppet/pops/lookup/lookup_key_function_provider.rb +9 -77
  47. data/lib/puppet/pops/lookup/module_data_provider.rb +27 -4
  48. data/lib/puppet/pops/parser/lexer2.rb +1 -1
  49. data/lib/puppet/pops/pcore.rb +3 -3
  50. data/lib/puppet/pops/types/p_object_type.rb +4 -6
  51. data/lib/puppet/pops/types/ruby_generator.rb +2 -2
  52. data/lib/puppet/pops/types/type_asserter.rb +3 -3
  53. data/lib/puppet/pops/types/type_mismatch_describer.rb +25 -7
  54. data/lib/puppet/pops/types/types.rb +20 -29
  55. data/lib/puppet/provider/exec.rb +4 -2
  56. data/lib/puppet/provider/nameservice.rb +8 -8
  57. data/lib/puppet/provider/selmodule/semodule.rb +20 -16
  58. data/lib/puppet/provider/service/src.rb +39 -39
  59. data/lib/puppet/provider/service/systemd.rb +1 -1
  60. data/lib/puppet/provider/user/aix.rb +7 -2
  61. data/lib/puppet/settings.rb +30 -17
  62. data/lib/puppet/ssl/base.rb +14 -1
  63. data/lib/puppet/ssl/certificate_authority.rb +4 -2
  64. data/lib/puppet/ssl/configuration.rb +4 -1
  65. data/lib/puppet/ssl/inventory.rb +10 -3
  66. data/lib/puppet/ssl/key.rb +7 -3
  67. data/lib/puppet/test/test_helper.rb +3 -0
  68. data/lib/puppet/type.rb +13 -1
  69. data/lib/puppet/type/exec.rb +16 -1
  70. data/lib/puppet/type/group.rb +17 -11
  71. data/lib/puppet/type/user.rb +3 -1
  72. data/lib/puppet/util.rb +1 -0
  73. data/lib/puppet/util/character_encoding.rb +95 -0
  74. data/lib/puppet/util/execution.rb +9 -6
  75. data/lib/puppet/util/reference.rb +4 -2
  76. data/lib/puppet/util/windows/file.rb +5 -1
  77. data/lib/puppet/version.rb +6 -2
  78. data/locales/config.yaml +1 -1
  79. data/locales/puppet.pot +18 -4
  80. data/spec/integration/ssl/autosign_spec.rb +18 -3
  81. data/spec/integration/ssl/key_spec.rb +104 -0
  82. data/spec/integration/type/user_spec.rb +13 -6
  83. data/spec/spec_helper.rb +7 -0
  84. data/spec/unit/application/inspect_spec.rb +9 -2
  85. data/spec/unit/data_providers/function_data_provider_spec.rb +2 -2
  86. data/spec/unit/etc_spec.rb +234 -0
  87. data/spec/unit/face/certificate_spec.rb +10 -2
  88. data/spec/unit/functions/dig_spec.rb +1 -1
  89. data/spec/unit/functions/hiera_spec.rb +40 -1
  90. data/spec/unit/functions/lookup_fixture_spec.rb +10 -10
  91. data/spec/unit/functions/lookup_spec.rb +1217 -357
  92. data/spec/unit/functions4_spec.rb +37 -1
  93. data/spec/unit/indirector/file_bucket_file/file_spec.rb +33 -2
  94. data/spec/unit/indirector/key/file_spec.rb +1 -1
  95. data/spec/unit/indirector/ssl_file_spec.rb +3 -3
  96. data/spec/unit/module_spec.rb +52 -59
  97. data/spec/unit/network/http/compression_spec.rb +39 -8
  98. data/spec/unit/parser/compiler_spec.rb +14 -0
  99. data/spec/unit/pops/loaders/loaders_spec.rb +21 -3
  100. data/spec/unit/pops/loaders/module_loaders_spec.rb +61 -0
  101. data/spec/unit/pops/lookup/context_spec.rb +56 -8
  102. data/spec/unit/pops/lookup/lookup_spec.rb +32 -1
  103. data/spec/unit/pops/parser/lexer2_spec.rb +8 -0
  104. data/spec/unit/pops/types/ruby_generator_spec.rb +48 -0
  105. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +12 -3
  106. data/spec/unit/pops/types/types_spec.rb +6 -7
  107. data/spec/unit/provider/nameservice_spec.rb +12 -12
  108. data/spec/unit/provider/package/pkg_spec.rb +2 -0
  109. data/spec/unit/provider/service/src_spec.rb +5 -0
  110. data/spec/unit/ssl/base_spec.rb +9 -0
  111. data/spec/unit/ssl/certificate_authority_spec.rb +2 -2
  112. data/spec/unit/ssl/certificate_request_attributes_spec.rb +6 -0
  113. data/spec/unit/ssl/certificate_request_spec.rb +1 -1
  114. data/spec/unit/ssl/certificate_spec.rb +1 -1
  115. data/spec/unit/ssl/configuration_spec.rb +11 -2
  116. data/spec/unit/ssl/inventory_spec.rb +27 -3
  117. data/spec/unit/ssl/key_spec.rb +7 -7
  118. data/spec/unit/type/exec_spec.rb +41 -4
  119. data/spec/unit/type/file_spec.rb +4 -1
  120. data/spec/unit/util/character_encoding_spec.rb +88 -0
  121. data/spec/unit/util/execution_spec.rb +12 -0
  122. data/spec/unit/version_spec.rb +4 -0
  123. metadata +3803 -3808
  124. data/tasks/i18n.rake +0 -20
@@ -97,4 +97,11 @@ class Puppet::Pops::Evaluator::CallableSignature
97
97
  def infinity?(x)
98
98
  x == Float::INFINITY
99
99
  end
100
+
101
+ # @return [Boolean] true if this signature represents an argument mismatch, false otherwise
102
+ #
103
+ # @api private
104
+ def argument_mismatch_handler?
105
+ false
106
+ end
100
107
  end
@@ -21,15 +21,24 @@ class Dispatch < Evaluator::CallableSignature
21
21
  # @api public
22
22
  attr_reader :block_name
23
23
 
24
+ # @param type [Puppet::Pops::Types::PArrayType, Puppet::Pops::Types::PTupleType] - type describing signature
25
+ # @param method_name [String] the name of the method that will be called when type matches given arguments
26
+ # @param param_names [Array<String>] names matching the number of parameters specified by type (or empty array)
27
+ # @param block_name [String,nil] name of block parameter, no nil
28
+ # @param injections [Array<Array>] injection data for weaved parameters
29
+ # @param weaving [Array<Integer,Array>] weaving knits
30
+ # @param last_captures [Boolean] true if last parameter is captures rest
31
+ # @param argument_mismatch_handler [Boolean] true if this is a dispatch for an argument mismatch
24
32
  # @api private
25
- def initialize(type, method_name, param_names, block_name, injections, weaving, last_captures)
33
+ def initialize(type, method_name, param_names, last_captures = false, block_name = nil, injections = EMPTY_ARRAY, weaving = EMPTY_ARRAY, argument_mismatch_handler = false)
26
34
  @type = type
27
35
  @method_name = method_name
28
- @param_names = param_names || []
36
+ @param_names = param_names
37
+ @last_captures = last_captures
29
38
  @block_name = block_name
30
- @injections = injections || []
39
+ @injections = injections
31
40
  @weaving = weaving
32
- @last_captures = last_captures
41
+ @argument_mismatch_handler = argument_mismatch_handler
33
42
  end
34
43
 
35
44
  # @api private
@@ -39,7 +48,11 @@ class Dispatch < Evaluator::CallableSignature
39
48
 
40
49
  # @api private
41
50
  def last_captures_rest?
42
- !! @last_captures
51
+ @last_captures
52
+ end
53
+
54
+ def argument_mismatch_handler?
55
+ @argument_mismatch_handler
43
56
  end
44
57
 
45
58
  # @api private
@@ -31,7 +31,12 @@ class Puppet::Pops::Functions::Dispatcher
31
31
  found = @dispatchers.find { |d| d.type.callable_with?(args, block) }
32
32
  unless found
33
33
  args_type = Puppet::Pops::Types::TypeCalculator.singleton.infer_set(block_given? ? args + [block] : args)
34
- raise ArgumentError, Puppet::Pops::Types::TypeMismatchDescriber.describe_signatures(instance.class.name, @dispatchers, args_type)
34
+ raise ArgumentError, Puppet::Pops::Types::TypeMismatchDescriber.describe_signatures(instance.class.name, signatures, args_type)
35
+ end
36
+
37
+ if found.argument_mismatch_handler?
38
+ msg = found.invoke(instance, calling_scope, args)
39
+ raise ArgumentError, "'#{instance.class.name}' #{msg}"
35
40
  end
36
41
 
37
42
  catch(:next) do
@@ -39,17 +44,6 @@ class Puppet::Pops::Functions::Dispatcher
39
44
  end
40
45
  end
41
46
 
42
- # Adds a regular dispatch for one method name
43
- #
44
- # @param type [Puppet::Pops::Types::PArrayType, Puppet::Pops::Types::PTupleType] - type describing signature
45
- # @param method_name [String] - the name of the method that will be called when type matches given arguments
46
- # @param names [Array<String>] - array with names matching the number of parameters specified by type (or empty array)
47
- #
48
- # @api private
49
- def add_dispatch(type, method_name, param_names, block_name, injections, weaving, last_captures)
50
- add(Puppet::Pops::Functions::Dispatch.new(type, method_name, param_names, block_name, injections, weaving, last_captures))
51
- end
52
-
53
47
  # Adds a dispatch directly to the set of dispatchers.
54
48
  # @api private
55
49
  def add(a_dispatch)
@@ -72,6 +66,6 @@ class Puppet::Pops::Functions::Dispatcher
72
66
 
73
67
  # @api private
74
68
  def signatures
75
- @dispatchers
69
+ @dispatchers.reject { |dispatcher| dispatcher.argument_mismatch_handler? }
76
70
  end
77
71
  end
@@ -69,7 +69,7 @@ class IssueReporter
69
69
  break if emitted >= max_errors
70
70
  end
71
71
  warnings_message = (emit_warnings && warnings.size > 0) ? ", and #{warnings.size} warnings" : ""
72
- giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up"
72
+ giving_up_message = "Language validation logged #{errors.size} errors#{warnings_message}. Giving up"
73
73
  exception = emit_exception.new(giving_up_message)
74
74
  exception.file = errors[0].file
75
75
  raise exception
@@ -738,5 +738,89 @@ module Issues
738
738
  ILLEGAL_BOM = hard_issue :ILLEGAL_BOM, :format_name, :bytes do
739
739
  "Illegal #{format_name} Byte Order mark at beginning of input: #{bytes} - remove these from the puppet source"
740
740
  end
741
+
742
+ HIERA_UNSUPPORTED_VERSION = hard_issue :HIERA_UNSUPPORTED_VERSION, :version do
743
+ "This runtime does not support hiera.yaml version #{version}"
744
+ end
745
+
746
+ HIERA_VERSION_3_NOT_GLOBAL = hard_issue :HIERA_VERSION_3_NOT_GLOBAL, :where do
747
+ "hiera.yaml version 3 cannot be used in #{label.a_an(where)}"
748
+ end
749
+
750
+ HIERA_UNSUPPORTED_VERSION_IN_GLOBAL = hard_issue :HIERA_UNSUPPORTED_VERSION_IN_GLOBAL do
751
+ 'hiera.yaml version 4 cannot be used in the global layer'
752
+ end
753
+
754
+ HIERA_UNDEFINED_VARIABLE = hard_issue :HIERA_UNDEFINED_VARIABLE, :name do
755
+ "Undefined variable '#{name}'"
756
+ end
757
+
758
+ HIERA_BACKEND_MULTIPLY_DEFINED = hard_issue :HIERA_BACKEND_MULTIPLY_DEFINED, :name, :first_line do
759
+ msg = "Backend '#{name}' is defined more than once"
760
+ fl = first_line
761
+ fl ? "#{msg}. First defined at line #{fl}" : msg
762
+ end
763
+
764
+ HIERA_NO_PROVIDER_FOR_BACKEND = hard_issue :HIERA_NO_PROVIDER_FOR_BACKEND, :name do
765
+ "No data provider is registered for backend '#{name}'"
766
+ end
767
+
768
+ HIERA_HIERARCHY_NAME_MULTIPLY_DEFINED = hard_issue :HIERA_HIERARCHY_NAME_MULTIPLY_DEFINED, :name, :first_line do
769
+ msg = "Hierarchy name '#{name}' defined more than once"
770
+ fl = first_line
771
+ fl ? "#{msg}. First defined at line #{fl}" : msg
772
+ end
773
+
774
+ HIERA_V3_BACKEND_NOT_GLOBAL = hard_issue :HIERA_V3_BACKEND_NOT_GLOBAL do
775
+ "'hiera3_backend' is only allowed in the global layer"
776
+ end
777
+
778
+ HIERA_DEFAULT_HIERARCHY_NOT_IN_MODULE = hard_issue :HIERA_DEFAULT_HIERARCHY_NOT_IN_MODULE do
779
+ "'default_hierarchy' is only allowed in the module layer"
780
+ end
781
+
782
+ HIERA_V3_BACKEND_REPLACED_BY_DATA_HASH = hard_issue :HIERA_V3_BACKEND_REPLACED_BY_DATA_HASH, :function_name do
783
+ "Use \"data_hash: #{function_name}_data\" instead of \"hiera3_backend: #{function_name}\""
784
+ end
785
+
786
+ HIERA_MISSING_DATA_PROVIDER_FUNCTION = hard_issue :HIERA_MISSING_DATA_PROVIDER_FUNCTION, :name do
787
+ "One of #{label.combine_strings(Lookup::HieraConfig::FUNCTION_KEYS)} must be defined in hierarchy '#{name}'"
788
+ end
789
+
790
+ HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS = hard_issue :HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS, :name do
791
+ "Only one of #{label.combine_strings(Lookup::HieraConfig::FUNCTION_KEYS)} can be defined in hierarchy '#{name}'"
792
+ end
793
+
794
+ HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS_IN_DEFAULT = hard_issue :HIERA_MULTIPLE_DATA_PROVIDER_FUNCTIONS_IN_DEFAULT do
795
+ "Only one of #{label.combine_strings(Lookup::HieraConfig::FUNCTION_KEYS)} can be defined in defaults"
796
+ end
797
+
798
+ HIERA_MULTIPLE_LOCATION_SPECS = hard_issue :HIERA_MULTIPLE_LOCATION_SPECS, :name do
799
+ "Only one of #{label.combine_strings(Lookup::HieraConfig::LOCATION_KEYS)} can be defined in hierarchy '#{name}'"
800
+ end
801
+
802
+ HIERA_OPTION_RESERVED_BY_PUPPET = hard_issue :HIERA_OPTION_RESERVED_BY_PUPPET, :key, :name do
803
+ "Option key '#{key}' used in hierarchy '#{name}' is reserved by Puppet"
804
+ end
805
+
806
+ HIERA_DEFAULT_OPTION_RESERVED_BY_PUPPET = hard_issue :HIERA_DEFAULT_OPTION_RESERVED_BY_PUPPET, :key do
807
+ "Option key '#{key}' used in defaults is reserved by Puppet"
808
+ end
809
+
810
+ HIERA_DATA_PROVIDER_FUNCTION_NOT_FOUND = hard_issue :HIERA_DATA_PROVIDER_FUNCTION_NOT_FOUND, :function_type, :function_name do
811
+ "Unable to find '#{function_type}' function named '#{function_name}'"
812
+ end
813
+
814
+ HIERA_INTERPOLATION_ALIAS_NOT_ENTIRE_STRING = hard_issue :HIERA_INTERPOLATION_ALIAS_NOT_ENTIRE_STRING do
815
+ "'alias' interpolation is only permitted if the expression is equal to the entire string"
816
+ end
817
+
818
+ HIERA_INTERPOLATION_UNKNOWN_INTERPOLATION_METHOD = hard_issue :HIERA_INTERPOLATION_UNKNOWN_INTERPOLATION_METHOD, :name do
819
+ "Unknown interpolation method '#{name}'"
820
+ end
821
+
822
+ HIERA_INTERPOLATION_METHOD_SYNTAX_NOT_ALLOWED = hard_issue :HIERA_INTERPOLATION_METHOD_SYNTAX_NOT_ALLOWED do
823
+ 'Interpolation using method syntax is not allowed in this context'
824
+ end
741
825
  end
742
826
  end
@@ -136,13 +136,21 @@ class BaseLoader < Loader
136
136
  # 4. give up
137
137
  #
138
138
  def internal_load(typed_name)
139
- # avoid calling get_entry, by looking it up
139
+ # avoid calling get_entry by looking it up
140
140
  te = @named_values[typed_name]
141
- te = parent.load_typed(typed_name) if te.nil? || te.value.nil?
142
- te = find(typed_name) if te.nil? || te.value.nil?
143
- te
144
- end
141
+ return te unless te.nil? || te.value.nil?
142
+
143
+ te = parent.load_typed(typed_name)
144
+ return te unless te.nil? || te.value.nil?
145
145
 
146
+ # Under some circumstances, the call to the parent loader will have resulted in files being
147
+ # parsed that in turn contained references to the requested entity and hence, caused a
148
+ # recursive call into this loader. This means that the entry might be present now, so a new
149
+ # check must be made.
150
+ te = @named_values[typed_name]
151
+ te.nil? || te.value.nil? ? find(typed_name) : te
152
+ end
146
153
  end
147
154
  end
148
155
  end
156
+
@@ -13,7 +13,13 @@ class ConfiguredDataProvider
13
13
  end
14
14
 
15
15
  def config(lookup_invocation)
16
- @config ||= assert_config_version(HieraConfig.create(lookup_invocation, configuration_path(lookup_invocation)))
16
+ @config ||= assert_config_version(HieraConfig.create(lookup_invocation, configuration_path(lookup_invocation), self))
17
+ end
18
+
19
+ # Needed to assign generated version 4 config
20
+ # @deprecated
21
+ def config=(config)
22
+ @config = config
17
23
  end
18
24
 
19
25
  # @return [Pathname] the path to the configuration
@@ -46,7 +52,7 @@ class ConfiguredDataProvider
46
52
  lookup_invocation.report_not_found(key)
47
53
  throw :no_such_key
48
54
  end
49
- merge_strategy.lookup(data_providers(lookup_invocation), lookup_invocation) do |data_provider|
55
+ merge_strategy.lookup(dps, lookup_invocation) do |data_provider|
50
56
  data_provider.unchecked_key_lookup(key, lookup_invocation, merge_strategy)
51
57
  end
52
58
  end
@@ -14,42 +14,132 @@ class DataDigFunctionProvider < FunctionProvider
14
14
  # @return [Object] the found object
15
15
  # @throw :no_such_key when the object is not found
16
16
  def unchecked_key_lookup(key, lookup_invocation, merge)
17
- segments = key.to_a
18
17
  lookup_invocation.with(:data_provider, self) do
19
18
  MergeStrategy.strategy(merge).lookup(locations, lookup_invocation) do |location|
20
- if location.nil?
21
- value = data_dig(key, segments, lookup_invocation, nil)
22
- lookup_invocation.report_found(key, validate_data_value(self, value))
23
- key.undig(value)
24
- else
25
- lookup_invocation.with(:location, location) do
26
- if location.exist?
27
- value = data_dig(key, segments, lookup_invocation, location.location)
28
- lookup_invocation.report_found(key, validate_data_value(self, value))
29
- key.undig(value)
30
- else
31
- lookup_invocation.report_path_not_found
32
- throw :no_such_key
33
- end
34
- end
35
- end
19
+ invoke_with_location(lookup_invocation, location, key, merge)
36
20
  end
37
21
  end
38
22
  end
39
23
 
24
+ def invoke_with_location(lookup_invocation, location, key, merge)
25
+ if location.nil?
26
+ key.undig(lookup_invocation.report_found(key, validated_data_dig(key, lookup_invocation, nil, merge)))
27
+ else
28
+ lookup_invocation.with(:location, location) do
29
+ key.undig(lookup_invocation.report_found(key, validated_data_dig(key, lookup_invocation, location, merge)))
30
+ end
31
+ end
32
+ end
33
+
34
+ def label
35
+ 'Data Dig'
36
+ end
37
+
38
+ def validated_data_dig(key, lookup_invocation, location, merge)
39
+ validate_data_value(data_dig(key, lookup_invocation, location, merge)) do
40
+ msg = "Value for key '#{key}', returned from #{full_name}"
41
+ location.nil? ? msg : "#{msg}, when using location '#{location}',"
42
+ end
43
+ end
44
+
40
45
  private
41
46
 
42
- def data_dig(key, segments, lookup_invocation, location)
47
+ def data_dig(key, lookup_invocation, location, merge)
48
+ unless location.nil? || location.exist?
49
+ lookup_invocation.report_location_not_found
50
+ throw :no_such_key
51
+ end
43
52
  ctx = function_context(lookup_invocation, location)
44
53
  ctx.data_hash ||= {}
45
54
  catch(:no_such_key) do
46
55
  hash = ctx.data_hash
47
- hash[key] = ctx.function.call(lookup_invocation.scope, segments, options(location), Context.new(ctx, lookup_invocation)) unless hash.include?(key)
56
+ hash[key] = ctx.function.call(lookup_invocation.scope, key.to_a, options(location), Context.new(ctx, lookup_invocation)) unless hash.include?(key)
48
57
  return hash[key]
49
58
  end
50
59
  lookup_invocation.report_not_found(key)
51
60
  throw :no_such_key
52
61
  end
53
62
  end
63
+
64
+ # @api private
65
+ class V3BackendFunctionProvider < DataDigFunctionProvider
66
+ TAG = 'hiera3_backend'.freeze
67
+
68
+ def data_dig(key, lookup_invocation, location, merge)
69
+ @backend ||= instantiate_backend(lookup_invocation)
70
+
71
+ # A merge_behavior retrieved from hiera.yaml must not be converted here. Instead, passing the symbol :hash
72
+ # tells the V3 backend to pick it up from the config.
73
+ resolution_type = lookup_invocation.hiera_v3_merge_behavior? ? :hash : convert_merge(merge)
74
+ @backend.lookup(key.to_s, lookup_invocation.scope, lookup_invocation.hiera_v3_location_overrides, resolution_type, context = {:recurse_guard => nil})
75
+ end
76
+
77
+ def full_name
78
+ "hiera version 3 backend '#{options[HieraConfig::KEY_BACKEND]}'"
79
+ end
80
+
81
+ def value_is_validated?
82
+ false
83
+ end
84
+
85
+ private
86
+
87
+ def instantiate_backend(lookup_invocation)
88
+ backend_name = options[HieraConfig::KEY_BACKEND]
89
+ begin
90
+ require 'hiera/backend'
91
+ require "hiera/backend/#{backend_name.downcase}_backend"
92
+ backend = Hiera::Backend.const_get("#{backend_name.capitalize}_backend").new
93
+ return backend.method(:lookup).arity == 4 ? Hiera::Backend::Backend1xWrapper.new(backend) : backend
94
+ rescue LoadError => e
95
+ lookup_invocation.report_text { "Unable to load backend '#{backend_name}': #{e.message}" }
96
+ throw :no_such_key
97
+ rescue NameError => e
98
+ lookup_invocation.report_text { "Unable to instantiate backend '#{backend_name}': #{e.message}" }
99
+ throw :no_such_key
100
+ end
101
+ end
102
+
103
+ # Converts a lookup 'merge' parameter argument into a Hiera 'resolution_type' argument.
104
+ #
105
+ # @param merge [String,Hash,nil] The lookup 'merge' argument
106
+ # @return [Symbol,Hash,nil] The Hiera 'resolution_type'
107
+ def convert_merge(merge)
108
+ case merge
109
+ when nil
110
+ when 'first', 'default'
111
+ # Nil is OK. Defaults to Hiera :priority
112
+ nil
113
+ when Puppet::Pops::MergeStrategy
114
+ convert_merge(merge.configuration)
115
+ when 'unique'
116
+ # Equivalent to Hiera :array
117
+ :array
118
+ when 'hash'
119
+ # Equivalent to Hiera :hash with default :native merge behavior. A Hash must be passed here
120
+ # to override possible Hiera deep merge config settings.
121
+ { :behavior => :native }
122
+ when 'deep', 'unconstrained_deep'
123
+ # Equivalent to Hiera :hash with :deeper merge behavior.
124
+ { :behavior => :deeper }
125
+ when 'reverse_deep'
126
+ # Equivalent to Hiera :hash with :deep merge behavior.
127
+ { :behavior => :deep }
128
+ when Hash
129
+ strategy = merge['strategy']
130
+ case strategy
131
+ when 'deep', 'unconstrained_deep', 'reverse_deep'
132
+ result = { :behavior => strategy == 'reverse_deep' ? :deep : :deeper }
133
+ # Remaining entries must have symbolic keys
134
+ merge.each_pair { |k,v| result[k.to_sym] = v unless k == 'strategy' }
135
+ result
136
+ else
137
+ convert_merge(strategy)
138
+ end
139
+ else
140
+ raise Puppet::DataBinding::LookupError, "Unrecognized value for request 'merge' parameter: '#{merge}'"
141
+ end
142
+ end
143
+ end
54
144
  end
55
145
  end
@@ -10,6 +10,10 @@ class DataHashFunctionProvider < FunctionProvider
10
10
 
11
11
  TAG = 'data_hash'.freeze
12
12
 
13
+ def self.trusted_return_type
14
+ @trusted_return_type ||= Types::PHashType.new(DataProvider.key_type, DataProvider.value_type)
15
+ end
16
+
13
17
  # Performs a lookup with the assumption that a recursive check has been made.
14
18
  #
15
19
  # @param key [LookupKey] The key to lookup
@@ -34,7 +38,7 @@ class DataHashFunctionProvider < FunctionProvider
34
38
  else
35
39
  lookup_invocation.with(:location, location) do
36
40
  if location.exist?
37
- lookup_key(lookup_invocation, location.location, root_key)
41
+ lookup_key(lookup_invocation, location, root_key)
38
42
  else
39
43
  lookup_invocation.report_location_not_found
40
44
  throw :no_such_key
@@ -54,12 +58,19 @@ class DataHashFunctionProvider < FunctionProvider
54
58
  lookup_invocation.report_not_found(root_key)
55
59
  throw :no_such_key
56
60
  end
57
- interpolate(parent_data_provider.validate_data_value(self, value), lookup_invocation, true)
61
+ value = validate_data_value(value) do
62
+ msg = "Value for key '#{root_key}', in hash returned from #{full_name}"
63
+ location.nil? ? msg : "#{msg}, when using location '#{location}',"
64
+ end
65
+ interpolate(value, lookup_invocation, true)
58
66
  end
59
67
 
60
68
  def data_hash(lookup_invocation, location)
61
69
  ctx = function_context(lookup_invocation, location)
62
- ctx.data_hash ||= parent_data_provider.validate_data_hash(self, call_data_hash_function(ctx, lookup_invocation, location))
70
+ ctx.data_hash ||= parent_data_provider.validate_data_hash(call_data_hash_function(ctx, lookup_invocation, location)) do
71
+ msg = "Value returned from #{full_name}"
72
+ location.nil? ? msg : "#{msg}, when using location '#{location}',"
73
+ end
63
74
  end
64
75
 
65
76
  def call_data_hash_function(ctx, lookup_invocation, location)
@@ -100,7 +111,11 @@ class V4DataHashFunctionProvider < DataHashFunctionProvider
100
111
  TAG = 'v4_data_hash'.freeze
101
112
 
102
113
  def name
103
- "deprecated API function \"#{function_name}\""
114
+ "Deprecated API function \"#{function_name}\""
115
+ end
116
+
117
+ def full_name
118
+ "deprecated API function '#{function_name}'"
104
119
  end
105
120
 
106
121
  def call_data_hash_function(ctx, lookup_invocation, location)