puppet 4.8.2 → 4.9.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 (348) hide show
  1. checksums.yaml +15 -0
  2. data/CONTRIBUTING.md +25 -1
  3. data/Gemfile +6 -0
  4. data/Rakefile +1 -0
  5. data/ext/project_data.yaml +5 -1
  6. data/ext/windows/service/daemon.rb +2 -1
  7. data/install.rb +43 -6
  8. data/lib/hiera/puppet_function.rb +15 -17
  9. data/lib/hiera/scope.rb +12 -14
  10. data/lib/puppet.rb +52 -0
  11. data/lib/puppet/application/face_base.rb +4 -0
  12. data/lib/puppet/application/lookup.rb +4 -2
  13. data/lib/puppet/application/resource.rb +1 -1
  14. data/lib/puppet/data_providers.rb +6 -3
  15. data/lib/puppet/data_providers/data_adapter.rb +6 -0
  16. data/lib/puppet/data_providers/data_function_support.rb +7 -0
  17. data/lib/puppet/data_providers/function_env_data_provider.rb +7 -0
  18. data/lib/puppet/data_providers/function_module_data_provider.rb +6 -0
  19. data/lib/puppet/data_providers/hiera_config.rb +31 -10
  20. data/lib/puppet/data_providers/hiera_env_data_provider.rb +6 -0
  21. data/lib/puppet/data_providers/hiera_interpolate.rb +2 -1
  22. data/lib/puppet/data_providers/hiera_module_data_provider.rb +6 -0
  23. data/lib/puppet/data_providers/hiera_support.rb +6 -0
  24. data/lib/puppet/data_providers/json_data_provider_factory.rb +12 -0
  25. data/lib/puppet/data_providers/yaml_data_provider_factory.rb +12 -0
  26. data/lib/puppet/defaults.rb +25 -4
  27. data/lib/puppet/face/ca.rb +2 -0
  28. data/lib/puppet/face/certificate_request.rb +2 -0
  29. data/lib/puppet/face/certificate_revocation_list.rb +2 -0
  30. data/lib/puppet/face/file.rb +3 -0
  31. data/lib/puppet/face/help.rb +19 -17
  32. data/lib/puppet/face/help/face.erb +3 -0
  33. data/lib/puppet/face/key.rb +1 -0
  34. data/lib/puppet/face/man.rb +4 -2
  35. data/lib/puppet/face/module/generate.rb +1 -1
  36. data/lib/puppet/face/status.rb +2 -0
  37. data/lib/puppet/feature/base.rb +9 -2
  38. data/lib/puppet/feature/hocon.rb +3 -0
  39. data/lib/puppet/file_system.rb +15 -3
  40. data/lib/puppet/file_system/windows.rb +8 -0
  41. data/lib/puppet/forge.rb +6 -6
  42. data/lib/puppet/forge/repository.rb +1 -2
  43. data/lib/puppet/functions/binary_file.rb +4 -10
  44. data/lib/puppet/functions/hiera_array.rb +1 -1
  45. data/lib/puppet/functions/hiera_include.rb +1 -1
  46. data/lib/puppet/functions/hocon_data.rb +24 -0
  47. data/lib/puppet/functions/json_data.rb +18 -0
  48. data/lib/puppet/functions/yaml_data.rb +21 -0
  49. data/lib/puppet/generate/type.rb +1 -1
  50. data/lib/puppet/graph/simple_graph.rb +4 -2
  51. data/lib/puppet/indirector/file_bucket_file/file.rb +10 -2
  52. data/lib/puppet/indirector/request.rb +5 -1
  53. data/lib/puppet/interface.rb +14 -2
  54. data/lib/puppet/interface/face_collection.rb +1 -1
  55. data/lib/puppet/module.rb +14 -2
  56. data/lib/puppet/module_tool.rb +4 -4
  57. data/lib/puppet/module_tool/applications/builder.rb +3 -2
  58. data/lib/puppet/module_tool/applications/installer.rb +14 -14
  59. data/lib/puppet/module_tool/applications/upgrader.rb +13 -13
  60. data/lib/puppet/module_tool/installed_modules.rb +7 -7
  61. data/lib/puppet/module_tool/local_tarball.rb +3 -3
  62. data/lib/puppet/module_tool/metadata.rb +1 -1
  63. data/lib/puppet/network/http/connection.rb +2 -0
  64. data/lib/puppet/network/http/webrick.rb +2 -1
  65. data/lib/puppet/parser/functions/hiera.rb +14 -0
  66. data/lib/puppet/parser/functions/hiera_array.rb +14 -0
  67. data/lib/puppet/parser/functions/hiera_hash.rb +14 -0
  68. data/lib/puppet/parser/functions/hiera_include.rb +14 -0
  69. data/lib/puppet/parser/scope.rb +14 -20
  70. data/lib/puppet/plugins/data_providers.rb +2 -0
  71. data/lib/puppet/plugins/data_providers/data_provider.rb +108 -17
  72. data/lib/puppet/plugins/data_providers/registry.rb +2 -36
  73. data/lib/puppet/pops.rb +6 -9
  74. data/lib/puppet/pops/adaptable.rb +0 -3
  75. data/lib/puppet/pops/binder/producers.rb +3 -3
  76. data/lib/puppet/pops/evaluator/access_operator.rb +4 -4
  77. data/lib/puppet/pops/evaluator/closure.rb +1 -1
  78. data/lib/puppet/pops/evaluator/compare_operator.rb +4 -4
  79. data/lib/puppet/pops/evaluator/evaluator_impl.rb +1 -1
  80. data/lib/puppet/pops/issues.rb +4 -0
  81. data/lib/puppet/pops/label_provider.rb +14 -0
  82. data/lib/puppet/pops/loader/loader_paths.rb +3 -1
  83. data/lib/puppet/pops/loader/module_loaders.rb +21 -7
  84. data/lib/puppet/pops/loader/typed_name.rb +0 -2
  85. data/lib/puppet/pops/loaders.rb +31 -12
  86. data/lib/puppet/pops/lookup.rb +4 -3
  87. data/lib/puppet/pops/lookup/configured_data_provider.rb +87 -0
  88. data/lib/puppet/pops/lookup/context.rb +121 -71
  89. data/lib/puppet/pops/lookup/data_adapter.rb +27 -0
  90. data/lib/puppet/pops/lookup/data_dig_function_provider.rb +55 -0
  91. data/lib/puppet/pops/lookup/data_hash_function_provider.rb +111 -0
  92. data/lib/puppet/pops/lookup/data_provider.rb +102 -0
  93. data/lib/puppet/pops/lookup/environment_data_provider.rb +27 -0
  94. data/lib/puppet/pops/lookup/explainer.rb +122 -82
  95. data/lib/puppet/pops/lookup/function_provider.rb +82 -0
  96. data/lib/puppet/pops/lookup/global_data_provider.rb +49 -0
  97. data/lib/puppet/pops/lookup/hiera_config.rb +601 -0
  98. data/lib/puppet/pops/lookup/interpolation.rb +56 -35
  99. data/lib/puppet/pops/lookup/invocation.rb +179 -101
  100. data/lib/puppet/pops/lookup/location_resolver.rb +72 -0
  101. data/lib/puppet/pops/lookup/lookup_adapter.rb +451 -0
  102. data/lib/puppet/pops/lookup/lookup_key.rb +99 -0
  103. data/lib/puppet/pops/lookup/lookup_key_function_provider.rb +119 -0
  104. data/lib/puppet/pops/lookup/module_data_provider.rb +58 -0
  105. data/lib/puppet/pops/lookup/sub_lookup.rb +8 -4
  106. data/lib/puppet/pops/merge_strategy.rb +120 -39
  107. data/lib/puppet/pops/parser/egrammar.ra +2 -0
  108. data/lib/puppet/pops/parser/eparser.rb +816 -808
  109. data/lib/puppet/pops/parser/locator.rb +3 -3
  110. data/lib/puppet/pops/parser/slurp_support.rb +4 -3
  111. data/lib/puppet/pops/pcore.rb +21 -12
  112. data/lib/puppet/pops/serialization/abstract_reader.rb +17 -7
  113. data/lib/puppet/pops/serialization/abstract_writer.rb +27 -12
  114. data/lib/puppet/pops/serialization/deserializer.rb +17 -4
  115. data/lib/puppet/pops/serialization/extension.rb +37 -8
  116. data/lib/puppet/pops/serialization/object.rb +14 -6
  117. data/lib/puppet/pops/serialization/rgen.rb +2 -1
  118. data/lib/puppet/pops/serialization/serializer.rb +30 -7
  119. data/lib/puppet/pops/types/implementation_registry.rb +1 -1
  120. data/lib/puppet/pops/types/p_object_type.rb +55 -12
  121. data/lib/puppet/pops/types/p_sem_ver_range_type.rb +27 -27
  122. data/lib/puppet/pops/types/p_sem_ver_type.rb +12 -12
  123. data/lib/puppet/pops/types/p_timespan_type.rb +6 -6
  124. data/lib/puppet/pops/types/p_timestamp_type.rb +2 -2
  125. data/lib/puppet/pops/types/p_type_set_type.rb +7 -16
  126. data/lib/puppet/pops/types/recursion_guard.rb +64 -20
  127. data/lib/puppet/pops/types/ruby_generator.rb +10 -0
  128. data/lib/puppet/pops/types/type_calculator.rb +23 -13
  129. data/lib/puppet/pops/types/type_factory.rb +20 -9
  130. data/lib/puppet/pops/types/type_formatter.rb +37 -17
  131. data/lib/puppet/pops/types/type_mismatch_describer.rb +7 -6
  132. data/lib/puppet/pops/types/type_parser.rb +6 -0
  133. data/lib/puppet/pops/types/types.rb +225 -132
  134. data/lib/puppet/pops/validation.rb +1 -1
  135. data/lib/puppet/pops/validation/checker4_0.rb +12 -2
  136. data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
  137. data/lib/puppet/pops/visitor.rb +4 -3
  138. data/lib/puppet/provider/mcx/mcxcontent.rb +2 -1
  139. data/lib/puppet/provider/nameservice.rb +15 -0
  140. data/lib/puppet/provider/package/appdmg.rb +1 -1
  141. data/lib/puppet/provider/package/dnf.rb +1 -1
  142. data/lib/puppet/provider/package/pkg.rb +1 -1
  143. data/lib/puppet/provider/package/pkgdmg.rb +1 -1
  144. data/lib/puppet/provider/package/pkgng.rb +1 -1
  145. data/lib/puppet/provider/package/rpm.rb +2 -2
  146. data/lib/puppet/provider/service/smf.rb +3 -3
  147. data/lib/puppet/provider/service/systemd.rb +5 -1
  148. data/lib/puppet/provider/user/directoryservice.rb +1 -0
  149. data/lib/puppet/provider/user/user_role_add.rb +15 -0
  150. data/lib/puppet/provider/yumrepo/inifile.rb +2 -2
  151. data/lib/puppet/provider/zone/solaris.rb +4 -1
  152. data/lib/puppet/reference/indirection.rb +1 -1
  153. data/lib/puppet/resource.rb +2 -3
  154. data/lib/puppet/resource/catalog.rb +12 -4
  155. data/lib/puppet/resource/type.rb +3 -3
  156. data/lib/puppet/settings.rb +1 -1
  157. data/lib/puppet/settings/config_file.rb +2 -1
  158. data/lib/puppet/settings/directory_setting.rb +6 -0
  159. data/lib/puppet/settings/environment_conf.rb +6 -2
  160. data/lib/puppet/settings/file_or_directory_setting.rb +6 -0
  161. data/lib/puppet/settings/file_setting.rb +10 -0
  162. data/lib/puppet/ssl/certificate_authority.rb +13 -2
  163. data/lib/puppet/ssl/host.rb +23 -1
  164. data/lib/puppet/transaction/additional_resource_generator.rb +7 -0
  165. data/lib/puppet/type/user.rb +16 -3
  166. data/lib/puppet/util.rb +1 -0
  167. data/lib/puppet/util/execution.rb +3 -3
  168. data/lib/puppet/util/filetype.rb +11 -5
  169. data/lib/puppet/util/logging.rb +2 -1
  170. data/lib/puppet/util/network_device/config.rb +1 -1
  171. data/lib/puppet/util/plist.rb +6 -0
  172. data/lib/puppet/util/profiler/aggregate.rb +1 -1
  173. data/lib/puppet/util/rdoc/generators/puppet_generator.rb +2 -2
  174. data/lib/puppet/util/rdoc/parser/puppet_parser_core.rb +2 -1
  175. data/lib/puppet/util/windows/adsi.rb +15 -12
  176. data/lib/puppet/vendor/load_semantic_puppet.rb +1 -0
  177. data/lib/puppet/vendor/pathspec/lib/pathspec.rb +2 -1
  178. data/lib/puppet/vendor/require_vendored.rb +0 -1
  179. data/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet.rb +17 -0
  180. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/dependency.rb +7 -7
  181. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/dependency/graph.rb +2 -2
  182. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/dependency/graph_node.rb +2 -2
  183. data/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet/dependency/module_release.rb +58 -0
  184. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/dependency/source.rb +2 -2
  185. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/dependency/unsatisfiable_graph.rb +2 -2
  186. data/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet/gem_version.rb +3 -0
  187. data/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet/locales/config.yaml +21 -0
  188. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/version.rb +48 -21
  189. data/lib/puppet/vendor/{semantic/lib/semantic → semantic_puppet/lib/semantic_puppet}/version_range.rb +15 -17
  190. data/lib/puppet/version.rb +1 -1
  191. data/lib/semver.rb +19 -12
  192. data/locales/config.yaml +29 -0
  193. data/locales/puppet.pot +79 -0
  194. data/man/man5/puppet.conf.5 +1 -1
  195. data/spec/fixtures/unit/application/environments/puppet_func_provider/functions/{data.pp → environment/data.pp} +0 -0
  196. data/spec/fixtures/unit/data_providers/environments/hiera_misc/data/common.yaml +2 -0
  197. data/spec/fixtures/unit/data_providers/environments/hiera_modules/modules/two/data/common.yaml +1 -1
  198. data/spec/fixtures/unit/data_providers/environments/sample/modules/backend/hiera.yaml +5 -0
  199. data/spec/fixtures/unit/data_providers/environments/sample/modules/backend/lib/puppet/bindings/backend/default.rb +9 -0
  200. data/spec/fixtures/unit/data_providers/environments/sample/modules/backend/lib/puppet_x/backend/special_data_provider_factory.rb +23 -0
  201. data/spec/fixtures/unit/data_providers/environments/sample/modules/backend/manifests/init.pp +5 -0
  202. data/spec/fixtures/unit/data_providers/environments/sample/modules/backend/metadata.json +9 -0
  203. data/spec/fixtures/unit/data_providers/environments/sample/modules/dataprovider/lib/puppet_x/helindbe/sample_env_data.rb +1 -0
  204. data/spec/fixtures/unit/data_providers/environments/sample/modules/dataprovider/manifests/init.pp +1 -1
  205. data/spec/fixtures/unit/functions/lookup/data/common.yaml +19 -0
  206. data/spec/fixtures/unit/functions/lookup_fixture/data/common.yaml +19 -0
  207. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/environment.conf +0 -0
  208. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/lib/puppet/functions/environment/data.rb +0 -0
  209. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/abc/lib/puppet/bindings/abc/default.rb +0 -0
  210. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/abc/lib/puppet/functions/abc/data.rb +0 -0
  211. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/abc/manifests/init.pp +0 -0
  212. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/bad_data/lib/puppet/bindings/bad_data/default.rb +0 -0
  213. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/bad_data/lib/puppet/functions/bad_data/data.rb +0 -0
  214. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/bad_data/manifests/init.pp +0 -0
  215. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/bca/lib/puppet/bindings/bca/default.rb +0 -0
  216. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/bca/lib/puppet/functions/bca/data.rb +0 -0
  217. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/bca/manifests/init.pp +0 -0
  218. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_json/data/empty.json +0 -0
  219. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_json/hiera.yaml +0 -0
  220. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_json/manifests/init.pp +0 -0
  221. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_json/metadata.json +0 -0
  222. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_json/data/empty_key.json +0 -0
  223. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_json/hiera.yaml +0 -0
  224. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_json/manifests/init.pp +0 -0
  225. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_json/metadata.json +0 -0
  226. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_yaml/data/empty_key.yaml +0 -0
  227. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_yaml/hiera.yaml +0 -0
  228. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_yaml/manifests/init.pp +0 -0
  229. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_key_yaml/metadata.json +0 -0
  230. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_yaml/data/empty.yaml +0 -0
  231. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_yaml/hiera.yaml +0 -0
  232. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_yaml/manifests/init.pp +0 -0
  233. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/empty_yaml/metadata.json +0 -0
  234. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/hieraprovider/data/first.json +0 -0
  235. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/hieraprovider/hiera.yaml +0 -0
  236. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/hieraprovider/manifests/init.pp +0 -0
  237. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/hieraprovider/metadata.json +0 -0
  238. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/meta/lib/puppet/functions/meta/data.rb +0 -0
  239. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/meta/manifests/init.pp +0 -0
  240. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/meta/metadata.json +0 -0
  241. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/metawcp/lib/puppet/bindings/metawcp/default.rb +0 -0
  242. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/metawcp/lib/puppet_x/thallgren/sample_module_data.rb +0 -0
  243. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/metawcp/manifests/init.pp +0 -0
  244. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/metawcp/metadata.json +0 -0
  245. data/spec/fixtures/unit/functions/{lookup → lookup_fixture}/environments/production/modules/no_provider/manifests/init.pp +0 -0
  246. data/spec/integration/application/apply_spec.rb +88 -2
  247. data/spec/integration/application/lookup_spec.rb +155 -0
  248. data/spec/integration/data_binding_spec.rb +5 -5
  249. data/spec/integration/defaults_spec.rb +13 -0
  250. data/spec/integration/environments/default_manifest_spec.rb +16 -16
  251. data/spec/integration/environments/setting_hooks_spec.rb +1 -1
  252. data/spec/integration/test/test_helper_spec.rb +6 -2
  253. data/spec/integration/transaction_spec.rb +74 -0
  254. data/spec/integration/util/execution_spec.rb +8 -0
  255. data/spec/lib/puppet_spec/module_tool/shared_functions.rb +2 -2
  256. data/spec/lib/puppet_spec/module_tool/stub_source.rb +1 -1
  257. data/spec/lib/puppet_spec/unindent.rb +2 -2
  258. data/spec/unit/application/face_base_spec.rb +16 -0
  259. data/spec/unit/application/lookup_spec.rb +262 -227
  260. data/spec/unit/data_providers/{sample_data_provider_spec.rb → custom_data_provider_spec.rb} +14 -16
  261. data/spec/unit/data_providers/function_data_provider_spec.rb +2 -2
  262. data/spec/unit/data_providers/hiera_data_provider_spec.rb +60 -97
  263. data/spec/unit/defaults_spec.rb +1 -1
  264. data/spec/unit/face/ca_spec.rb +10 -0
  265. data/spec/unit/face/certificate_request_spec.rb +10 -0
  266. data/spec/unit/face/certificate_revocation_list_spec.rb +10 -0
  267. data/spec/unit/face/file_spec.rb +4 -0
  268. data/spec/unit/face/help_spec.rb +17 -0
  269. data/spec/unit/face/key_spec.rb +10 -0
  270. data/spec/unit/face/status_spec.rb +10 -0
  271. data/spec/unit/file_system_spec.rb +143 -6
  272. data/spec/unit/functions/binary_file_spec.rb +1 -1
  273. data/spec/unit/functions/hiera_spec.rb +257 -47
  274. data/spec/unit/functions/lookup_fixture_spec.rb +693 -0
  275. data/spec/unit/functions/lookup_spec.rb +1319 -608
  276. data/spec/unit/functions/new_spec.rb +3 -3
  277. data/spec/unit/graph/rb_tree_map_spec.rb +1 -1
  278. data/spec/unit/graph/simple_graph_spec.rb +1 -1
  279. data/spec/unit/hiera/scope_spec.rb +4 -4
  280. data/spec/unit/indirector/request_spec.rb +9 -0
  281. data/spec/unit/interface_spec.rb +27 -0
  282. data/spec/unit/man_spec.rb +1 -1
  283. data/spec/unit/module_spec.rb +1 -1
  284. data/spec/unit/module_tool/applications/builder_spec.rb +16 -1
  285. data/spec/unit/module_tool/applications/installer_spec.rb +1 -1
  286. data/spec/unit/module_tool/applications/upgrader_spec.rb +1 -1
  287. data/spec/unit/module_tool/installed_modules_spec.rb +6 -6
  288. data/spec/unit/module_tool_spec.rb +3 -3
  289. data/spec/unit/network/http/connection_spec.rb +10 -0
  290. data/spec/unit/network/http/webrick_spec.rb +1 -1
  291. data/spec/unit/pops/evaluator/access_ops_spec.rb +2 -2
  292. data/spec/unit/pops/evaluator/runtime3_converter_spec.rb +9 -9
  293. data/spec/unit/pops/loaders/environment_loader_spec.rb +172 -0
  294. data/spec/unit/pops/lookup/context_spec.rb +45 -16
  295. data/spec/unit/pops/lookup/interpolation_spec.rb +28 -20
  296. data/spec/unit/pops/lookup/lookup_spec.rb +197 -0
  297. data/spec/unit/pops/merge_strategy_spec.rb +18 -0
  298. data/spec/unit/pops/parser/lexer2_spec.rb +16 -1
  299. data/spec/unit/pops/parser/parse_site_spec.rb +4 -0
  300. data/spec/unit/pops/serialization/packer_spec.rb +4 -23
  301. data/spec/unit/pops/serialization/serialization_spec.rb +32 -8
  302. data/spec/unit/pops/types/p_object_type_spec.rb +68 -3
  303. data/spec/unit/pops/types/p_sem_ver_type_spec.rb +4 -4
  304. data/spec/unit/pops/types/p_type_set_type_spec.rb +31 -2
  305. data/spec/unit/pops/types/type_acceptor_spec.rb +18 -17
  306. data/spec/unit/pops/types/type_calculator_spec.rb +39 -40
  307. data/spec/unit/pops/types/type_factory_spec.rb +3 -3
  308. data/spec/unit/pops/types/type_formatter_spec.rb +7 -3
  309. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +13 -2
  310. data/spec/unit/pops/types/types_spec.rb +25 -2
  311. data/spec/unit/pops/validator/validator_spec.rb +60 -4
  312. data/spec/unit/provider/nameservice_spec.rb +42 -0
  313. data/spec/unit/provider/package/aptrpm_spec.rb +1 -1
  314. data/spec/unit/provider/package/pkg_spec.rb +22 -0
  315. data/spec/unit/provider/package/pkgng_spec.rb +12 -0
  316. data/spec/unit/provider/package/rpm_spec.rb +8 -8
  317. data/spec/unit/provider/service/smf_spec.rb +13 -11
  318. data/spec/unit/provider/service/systemd_spec.rb +8 -1
  319. data/spec/unit/provider/user/useradd_spec.rb +1 -0
  320. data/spec/unit/puppet_spec.rb +14 -0
  321. data/spec/unit/resource/catalog_spec.rb +15 -9
  322. data/spec/unit/resource_spec.rb +20 -17
  323. data/spec/unit/semver_spec.rb +14 -0
  324. data/spec/unit/ssl/certificate_authority_spec.rb +12 -1
  325. data/spec/unit/transaction/additional_resource_generator_spec.rb +11 -0
  326. data/spec/unit/type/user_spec.rb +32 -6
  327. data/spec/unit/util/filetype_spec.rb +3 -3
  328. data/spec/unit/util/yaml_spec.rb +1 -1
  329. data/spec/unit/util_spec.rb +10 -2
  330. data/tasks/i18n.rake +20 -0
  331. metadata +2661 -2593
  332. data/lib/puppet/data_providers/lookup_adapter.rb +0 -254
  333. data/lib/puppet/vendor/load_semantic.rb +0 -1
  334. data/lib/puppet/vendor/semantic/Gemfile +0 -20
  335. data/lib/puppet/vendor/semantic/PUPPET_README.md +0 -6
  336. data/lib/puppet/vendor/semantic/Rakefile +0 -69
  337. data/lib/puppet/vendor/semantic/lib/semantic.rb +0 -7
  338. data/lib/puppet/vendor/semantic/lib/semantic/dependency/module_release.rb +0 -60
  339. data/lib/puppet/vendor/semantic/spec/spec_helper.rb +0 -24
  340. data/lib/puppet/vendor/semantic/spec/unit/semantic/dependency/graph_node_spec.rb +0 -141
  341. data/lib/puppet/vendor/semantic/spec/unit/semantic/dependency/graph_spec.rb +0 -162
  342. data/lib/puppet/vendor/semantic/spec/unit/semantic/dependency/module_release_spec.rb +0 -143
  343. data/lib/puppet/vendor/semantic/spec/unit/semantic/dependency/source_spec.rb +0 -5
  344. data/lib/puppet/vendor/semantic/spec/unit/semantic/dependency/unsatisfiable_graph_spec.rb +0 -44
  345. data/lib/puppet/vendor/semantic/spec/unit/semantic/dependency_spec.rb +0 -383
  346. data/lib/puppet/vendor/semantic/spec/unit/semantic/version_range_spec.rb +0 -307
  347. data/lib/puppet/vendor/semantic/spec/unit/semantic/version_spec.rb +0 -608
  348. data/spec/fixtures/unit/data_providers/environments/sample/manifests/site.pp +0 -6
@@ -1,733 +1,1444 @@
1
1
  #! /usr/bin/env ruby
2
2
  require 'spec_helper'
3
3
  require 'puppet_spec/compiler'
4
+ require 'puppet_spec/files'
4
5
  require 'puppet/pops'
6
+ require 'deep_merge/core'
5
7
 
6
- describe "when performing lookup" do
8
+ describe "The lookup function" do
7
9
  include PuppetSpec::Compiler
8
-
9
- # Assembles code that includes the *abc* class and compiles it into a catalog. This class will use the global
10
- # variable $args to perform a lookup and assign the result to $abc::result. Unless the $block is set to
11
- # the string 'no_block_present', it will be passed as a lambda to the lookup. The assembled code will declare
12
- # a notify resource with a name that is formed by interpolating the result into a format string.
13
- #
14
- # The method performs the folloging steps.
15
- #
16
- # - Build the code that:
17
- # - sets the $args variable from _lookup_args_
18
- # - sets the $block parameter to the given block or the string 'no_block_present'
19
- # - includes the abc class
20
- # - assigns the $abc::result to $r
21
- # - interpolates a string using _fmt_ (which is assumed to use $r)
22
- # - declares a notify resource from the interpolated string
23
- # - Compile the code into a catalog
24
- # - Return the name of all Notify resources in that catalog
25
- #
26
- # @param fmt [String] The puppet interpolated string used when creating the notify title
27
- # @param *args [String] splat of args that will be concatenated to form the puppet args sent to lookup
28
- # @return [Array<String>] List of names of Notify resources in the resulting catalog
29
- #
30
- def assemble_and_compile(fmt, *lookup_args, &block)
31
- assemble_and_compile_with_block(fmt, "'no_block_present'", *lookup_args, &block)
32
- end
33
-
34
- def assemble_and_compile_with_block(fmt, block, *lookup_args, &cblock)
35
- compile_and_get_notifications(<<-END.gsub(/^ {6}/, ''), &cblock)
36
- $args = [#{lookup_args.join(',')}]
37
- $block = #{block}
38
- include abc
39
- $r = if $abc::result == undef { 'no_value' } else { $abc::result }
40
- notify { \"#{fmt}\": }
41
- END
42
- end
43
-
44
- def compile_and_get_notifications(code)
45
- Puppet[:code] = code
46
- catalog = block_given? ? compiler.compile { |catalog| yield(compiler.topscope); catalog } : compiler.compile
47
- catalog.resources.map(&:ref).select { |r| r.start_with?('Notify[') }.map { |r| r[7..-2] }
48
- end
49
-
50
- # There is a fully configured 'production' environment in fixtures at this location
51
- let(:environmentpath) { File.join(my_fixture_dir, 'environments') }
52
- let(:node) { Puppet::Node.new("testnode", :facts => Puppet::Node::Facts.new("facts", {}), :environment => 'production') }
53
- let(:compiler) { Puppet::Parser::Compiler.new(node) }
54
-
55
- around(:each) do |example|
56
- # Initialize settings to get a full compile as close as possible to a real
57
- # environment load
58
- Puppet.settings.initialize_global_settings
59
-
60
- # Initialize loaders based on the environmentpath. It does not work to
61
- # just set the setting environmentpath for some reason - this achieves the same:
62
- # - first a loader is created, loading directory environments from the fixture (there is
63
- # one environment, 'sample', which will be loaded since the node references this
64
- # environment by name).
65
- # - secondly, the created env loader is set as 'environments' in the puppet context.
66
- #
67
- environments = Puppet::Environments::Directories.new(environmentpath, [])
68
- Puppet.override(:environments => environments) do
69
- example.run
10
+ include PuppetSpec::Files
11
+
12
+ context 'with an environment' do
13
+ let(:env_name) { 'spec' }
14
+ let(:code_dir_files) { {} }
15
+ let(:code_dir) { tmpdir('code') }
16
+ let(:environment_files) do
17
+ {
18
+ env_name => {
19
+ 'modules' => {},
20
+ 'hiera.yaml' => <<-YAML.unindent,
21
+ ---
22
+ version: 5
23
+ hierarchy:
24
+ - name: "Common"
25
+ data_hash: yaml_data
26
+ path: "common.yaml"
27
+ YAML
28
+ 'data' => {
29
+ 'common.yaml' => <<-YAML.unindent
30
+ ---
31
+ a: value a
32
+ mod_a::a: value mod_a::a (from environment)
33
+ mod_a::hash_a:
34
+ a: value mod_a::hash_a.a (from environment)
35
+ mod_a::hash_b:
36
+ a: value mod_a::hash_b.a (from environment)
37
+ hash_b:
38
+ hash_ba:
39
+ bab: value hash_b.hash_ba.bab (from environment)
40
+ hash_c:
41
+ hash_ca:
42
+ caa: value hash_c.hash_ca.caa (from environment)
43
+ lookup_options:
44
+ mod_a::hash_b:
45
+ merge: hash
46
+ hash_c:
47
+ merge: hash
48
+ YAML
49
+ }
50
+ }
51
+ }
70
52
  end
71
- end
72
53
 
73
- context 'using normal parameters' do
74
- it 'can lookup value provided by the environment' do
75
- resources = assemble_and_compile('${r}', "'abc::a'")
76
- expect(resources).to include('env_a')
54
+ let(:logs) { [] }
55
+ let(:notices) { logs.select { |log| log.level == :notice }.map { |log| log.message } }
56
+ let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }
57
+ let(:debugs) { logs.select { |log| log.level == :debug }.map { |log| log.message } }
58
+ let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, env_name, 'modules')]) }
59
+ let(:environments) { Puppet::Environments::Directories.new(populated_env_dir, []) }
60
+ let(:node) { Puppet::Node.new('test_lookup', :environment => env) }
61
+ let(:compiler) { Puppet::Parser::Compiler.new(node) }
62
+ let(:lookup_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'lookup') }
63
+ let(:defaults) {
64
+ {
65
+ 'mod_a::xd' => 'value mod_a::xd (from default)',
66
+ 'mod_a::xd_found' => 'value mod_a::xd_found (from default)',
67
+ 'scope_xd' => 'value scope_xd (from default)'
68
+ }}
69
+ let(:overrides) {
70
+ {
71
+ 'mod_a::xo' => 'value mod_a::xo (from override)',
72
+ 'scope_xo' => 'value scope_xo (from override)'
73
+ }}
74
+ let(:invocation_with_explain) { Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true) }
75
+ let(:explanation) { invocation_with_explain.explainer.explain }
76
+
77
+ let(:populated_code_dir) do
78
+ dir_contained_in(code_dir, code_dir_files)
79
+ code_dir
77
80
  end
78
81
 
79
- it 'can lookup value provided by the module' do
80
- resources = assemble_and_compile('${r}', "'abc::b'")
81
- expect(resources).to include('module_b')
82
+ let(:env_dir) do
83
+ d = File.join(populated_code_dir, 'environments')
84
+ Dir.mkdir(d)
85
+ d
82
86
  end
83
87
 
84
- it "can lookup value provided by the module that has 'function' data_provider entry in metadata.json" do
85
- resources = compile_and_get_notifications("$args = ['meta::b']\ninclude meta\nnotify { $meta::result: }\n")
86
- expect(resources).to include('module_b')
88
+ let(:populated_env_dir) do
89
+ dir_contained_in(env_dir, environment_files)
90
+ env_dir
87
91
  end
88
92
 
89
- it "can lookup value provided by the module that has 'sample' data_provider entry in metadata.json" do
90
- resources = compile_and_get_notifications("$args = ['metawcp::b']\ninclude metawcp\nnotify { $metawcp::result: }\n")
91
- expect(resources).to include('module_b')
93
+ before(:each) do
94
+ Puppet.settings[:codedir] = code_dir
95
+ Puppet.push_context(:environments => environments, :current_environment => env)
92
96
  end
93
97
 
94
- it 'can lookup value provided in global scope' do
95
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
96
- Hiera.any_instance.expects(:lookup).with('abc::a', any_parameters).returns('global_a')
97
- resources = assemble_and_compile('${r}', "'abc::a'")
98
- expect(resources).to include('global_a')
98
+ after(:each) do
99
+ Puppet.pop_context
100
+ if Object.const_defined?(:Hiera)
101
+ Hiera.send(:remove_instance_variable, :@config) if Hiera.instance_variable_defined?(:@config)
102
+ Hiera.send(:remove_instance_variable, :@logger) if Hiera.instance_variable_defined?(:@logger)
103
+ if Hiera.const_defined?(:Config)
104
+ Hiera::Config.send(:remove_instance_variable, :@config) if Hiera::Config.instance_variable_defined?(:@config)
105
+ end
106
+ if Hiera.const_defined?(:Backend)
107
+ Hiera::Backend.clear!
108
+ end
109
+ end
99
110
  end
100
111
 
101
- it 'will stop at first found name when several names are provided' do
102
- resources = assemble_and_compile('${r}', "['abc::b', 'abc::a']")
103
- expect(resources).to include('module_b')
112
+ def collect_notices(code, explain = false, &block)
113
+ Puppet[:code] = code
114
+ Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
115
+ scope = compiler.topscope
116
+ scope['environment'] = env_name
117
+ scope['domain'] = 'example.com'
118
+ scope['scope_scalar'] = 'scope scalar value'
119
+ scope['scope_hash'] = { 'a' => 'scope hash a', 'b' => 'scope hash b' }
120
+ if explain
121
+ begin
122
+ invocation_with_explain.lookup('dummy', nil) do
123
+ if block_given?
124
+ compiler.compile { |catalog| block.call(compiler.topscope); catalog }
125
+ else
126
+ compiler.compile
127
+ end
128
+ end
129
+ rescue Puppet::DataBinding::LookupError => e
130
+ invocation_with_explain.report_text { e.message }
131
+ end
132
+ else
133
+ if block_given?
134
+ compiler.compile { |catalog| block.call(compiler.topscope); catalog }
135
+ else
136
+ compiler.compile
137
+ end
138
+ end
139
+ end
140
+ nil
104
141
  end
105
142
 
106
- it 'can lookup value provided by the module that is overriden by environment' do
107
- resources = assemble_and_compile('${r}', "'abc::c'")
108
- expect(resources).to include('env_c')
143
+ def lookup(key, options = {}, explain = false)
144
+ nc_opts = options.empty? ? '' : ", #{Puppet::Pops::Types::TypeFormatter.string(options)}"
145
+ keys = key.is_a?(Array) ? key : [key]
146
+ collect_notices(keys.map { |k| "notice(String(lookup('#{k}'#{nc_opts}), '%p'))" }.join("\n"), explain)
147
+ if explain
148
+ explanation
149
+ else
150
+ result = notices.map { |n| Puppet::Pops::Types::TypeParser.singleton.parse_literal(n) }
151
+ key.is_a?(Array) ? result : result[0]
152
+ end
109
153
  end
110
154
 
111
- it "can 'unique' merge values provided by both the module and the environment" do
112
- resources = assemble_and_compile('${r[0]}_${r[1]}', "'abc::c'", 'Array[String]', "'unique'")
113
- expect(resources).to include('env_c_module_c')
155
+ def explain(key, options = {})
156
+ lookup(key, options, true)[1]
157
+ explanation
114
158
  end
115
159
 
116
- it "can 'hash' merge values provided by the environment only" do
117
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::d'", 'Hash[String,String]', "'hash'")
118
- expect(resources).to include('env_d1_env_d2_env_d3')
160
+ it 'finds data in the environment' do
161
+ expect(lookup('a')).to eql('value a')
119
162
  end
120
163
 
121
- it "can 'hash' merge values provided by both the environment and the module" do
122
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "'hash'")
123
- expect(resources).to include('env_e1_module_e2_env_e3')
124
- end
164
+ context 'that has no lookup configured' do
165
+ let(:environment_files) do
166
+ {
167
+ env_name => {
168
+ 'modules' => {},
169
+ 'data' => {
170
+ 'common.yaml' => <<-YAML.unindent
171
+ ---
172
+ a: value a
173
+ YAML
174
+ }
175
+ }
176
+ }
177
+ end
125
178
 
126
- it "can 'hash' merge values provided by global, environment, and module" do
127
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
128
- Hiera.any_instance.expects(:lookup).with('abc::e', any_parameters).returns({ 'k1' => 'global_e1' })
129
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "'hash'")
130
- expect(resources).to include('global_e1_module_e2_env_e3')
131
- end
179
+ it 'does not find data in the environment' do
180
+ expect { lookup('a') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'a'/)
181
+ end
132
182
 
133
- it "can pass merge parameter in the form of a hash with a 'strategy=>unique'" do
134
- resources = assemble_and_compile('${r[0]}_${r[1]}', "'abc::c'", 'Array[String]', "{strategy => 'unique'}")
135
- expect(resources).to include('env_c_module_c')
136
- end
183
+ context "but an environment.conf with 'environment_data_provider=hiera'" do
184
+ let(:environment_files_1) do
185
+ DeepMerge.deep_merge!(environment_files, 'environment.conf' => "environment_data_provider=hiera\n")
186
+ end
137
187
 
138
- it "can pass merge parameter in the form of a hash with 'strategy=>hash'" do
139
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "{strategy => 'hash'}")
140
- expect(resources).to include('env_e1_module_e2_env_e3')
141
- end
188
+ let(:populated_env_dir) do
189
+ dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => environment_files_1))
190
+ env_dir
191
+ end
142
192
 
143
- it "can pass merge parameter in the form of a hash with a 'strategy=>deep'" do
144
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "{strategy => 'deep'}")
145
- expect(resources).to include('env_e1_module_e2_env_e3')
146
- end
193
+ it 'finds data in the environment and reports deprecation warning for environment.conf' do
194
+ expect(lookup('a')).to eql('value a')
195
+ expect(warnings).to include(/Defining environment_data_provider='hiera' in environment.conf is deprecated. A 'hiera.yaml' file should be used instead/)
196
+ end
147
197
 
148
- it "will fail unless merge in the form of a hash contains a 'strategy'" do
149
- expect do
150
- assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "{merge_key => 'hash'}")
151
- end.to raise_error(Puppet::ParseError, /hash given as 'merge' must contain the name of a strategy/)
152
- end
198
+ context 'and a hiera.yaml file' do
199
+ let(:environment_files_2) { DeepMerge.deep_merge!(environment_files_1,'hiera.yaml' => <<-YAML.unindent) }
200
+ ---
201
+ version: 4
202
+ hierarchy:
203
+ - name: common
204
+ backend: yaml
205
+ YAML
206
+
207
+ let(:populated_env_dir) do
208
+ dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => environment_files_2))
209
+ env_dir
210
+ end
211
+
212
+ it 'finds data in the environment and reports deprecation warnings for both environment.conf and hiera.yaml' do
213
+ expect(lookup('a')).to eql('value a')
214
+ expect(warnings).to include(/Defining environment_data_provider='hiera' in environment.conf is deprecated/)
215
+ expect(warnings).to include(/Use of 'hiera.yaml' version 4 is deprecated. It should be converted to version 5/)
216
+ end
217
+ end
218
+ end
153
219
 
154
- it 'will raise an exception when value is not found for single key and no default is provided' do
155
- expect do
156
- assemble_and_compile('${r}', "'abc::x'")
157
- end.to raise_error(Puppet::ParseError, /did not find a value for the name 'abc::x'/)
158
- end
220
+ context "but an environment.conf with 'environment_data_provider=function'" do
221
+ let(:environment_files) do
222
+ {
223
+ env_name => {
224
+ 'environment.conf' => "environment_data_provider=function\n",
225
+ 'functions' => {
226
+ 'environment' => { 'data.pp' => <<-PUPPET.unindent }
227
+ function environment::data() {
228
+ { 'a' => 'value a' }
229
+ }
230
+ PUPPET
231
+ }
232
+ }
233
+ }
234
+ end
159
235
 
160
- it 'can lookup an undef value' do
161
- resources = assemble_and_compile('${r}', "'abc::n'")
162
- expect(resources).to include('no_value')
236
+ it 'finds data in the environment and reports deprecation warning for environment.conf' do
237
+ expect(lookup('a')).to eql('value a')
238
+ expect(warnings).to include(/Defining environment_data_provider='function' in environment.conf is deprecated. A 'hiera.yaml' file should be used instead/)
239
+ expect(warnings).to include(/Using of legacy data provider function 'environment::data'. Please convert to a 'data_hash' function/)
240
+ end
241
+ end
163
242
  end
164
243
 
165
- it 'will not replace an undef value with a given default' do
166
- resources = assemble_and_compile('${r}', "'abc::n'", 'undef', 'undef', '"default_n"')
167
- expect(resources).to include('no_value')
168
- end
244
+ context 'that has interpolated paths configured' do
245
+ let(:environment_files) do
246
+ {
247
+ env_name => {
248
+ 'hiera.yaml' => <<-YAML.unindent,
249
+ ---
250
+ version: 5
251
+ hierarchy:
252
+ - name: "Varying"
253
+ data_hash: yaml_data
254
+ path: "x%{::var}.yaml"
255
+ YAML
256
+ 'modules' => {},
257
+ 'data' => {
258
+ 'x.yaml' => <<-YAML.unindent,
259
+ y: value y from x
260
+ YAML
261
+ 'x_d.yaml' => <<-YAML.unindent
262
+ y: value y from x_d
263
+ YAML
264
+ }
265
+ }
266
+ }
267
+ end
169
268
 
170
- it 'will not accept a succesful lookup of an undef value when the type rejects it' do
171
- expect do
172
- assemble_and_compile('${r}', "'abc::n'", 'String')
173
- end.to raise_error(Puppet::ParseError, /Found value has wrong type, expects a String value, got Undef/)
269
+ it 'reloads the configuration if interpolated values change' do
270
+ Puppet[:log_level] = 'debug'
271
+ collect_notices("notice('success')") do |scope|
272
+ expect(lookup_func.call(scope, 'y')).to eql('value y from x')
273
+ scope['var'] = '_d'
274
+ expect(lookup_func.call(scope, 'y')).to eql('value y from x_d')
275
+ end
276
+ expect(notices).to eql(['success'])
277
+ expect(debugs.any? { |m| m =~ /Hiera configuration recreated due to change of scope variables used in interpolation expressions/ }).to be_truthy
278
+ end
174
279
  end
175
280
 
176
- it 'will raise an exception when value is not found for array key and no default is provided' do
177
- expect do
178
- assemble_and_compile('${r}', "['abc::x', 'abc::y']")
179
- end.to raise_error(Puppet::ParseError, /did not find a value for any of the names \['abc::x', 'abc::y'\]/)
180
- end
281
+ context 'that uses reserved option' do
282
+ let(:environment_files) do
283
+ {
284
+ env_name => {
285
+ 'hiera.yaml' => <<-YAML.unindent,
286
+ ---
287
+ version: 5
288
+ hierarchy:
289
+ - name: "Illegal"
290
+ options:
291
+ #{opt_spec}
292
+ data_hash: yaml_data
293
+ YAML
294
+ 'data' => {
295
+ 'foo.yaml' => "a: The value a\n"
296
+ }
297
+ }
298
+ }
299
+ end
181
300
 
182
- it 'can lookup and deep merge shallow values provided by the environment only' do
183
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::d'", 'Hash[String,String]', "'deep'")
184
- expect(resources).to include('env_d1_env_d2_env_d3')
185
- end
301
+ context 'path' do
302
+ let(:opt_spec) { 'path: data/foo.yaml' }
186
303
 
187
- it 'can lookup and deep merge shallow values provided by both the module and the environment' do
188
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "'deep'")
189
- expect(resources).to include('env_e1_module_e2_env_e3')
190
- end
304
+ it 'fails and reports the reserved option key' do
305
+ expect { lookup('a') }.to raise_error do |e|
306
+ expect(e.message).to match(/Option key 'path' used in hierarchy 'Illegal' is reserved by Puppet/)
307
+ end
308
+ end
309
+ end
191
310
 
192
- it 'can lookup and deep merge deep values provided by global, environment, and module' do
193
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
194
- Hiera.any_instance.expects(:lookup).with('abc::f', any_parameters).returns({ 'k1' => { 's1' => 'global_f11' }, 'k2' => { 's3' => 'global_f23' }})
195
- resources = assemble_and_compile('${r[k1][s1]}_${r[k1][s2]}_${r[k1][s3]}_${r[k2][s1]}_${r[k2][s2]}_${r[k2][s3]}', "'abc::f'", 'Hash[String,Hash[String,String]]', "'deep'")
196
- expect(resources).to include('global_f11_env_f12_module_f13_env_f21_module_f22_global_f23')
197
- end
311
+ context 'uri' do
312
+ let(:opt_spec) { 'uri: file:///data/foo.yaml' }
198
313
 
199
- it 'will propagate resolution_type :array to Hiera when merge == \'unique\'' do
200
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
201
- Hiera.any_instance.expects(:lookup).with('abc::c', anything, anything, anything, :array).returns(['global_c'])
202
- resources = assemble_and_compile('${r[0]}_${r[1]}_${r[2]}', "'abc::c'", 'Array[String]', "'unique'")
203
- expect(resources).to include('global_c_env_c_module_c')
314
+ it 'fails and reports the reserved option key' do
315
+ expect { lookup('a') }.to raise_error do |e|
316
+ expect(e.message).to match(/Option key 'uri' used in hierarchy 'Illegal' is reserved by Puppet/)
317
+ end
318
+ end
319
+ end
204
320
  end
205
321
 
206
- it 'will propagate a Hash resolution_type with :behavior => :native to Hiera when merge == \'hash\'' do
207
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
208
- Hiera.any_instance.expects(:lookup).with('abc::e', anything, anything, anything, { :behavior => :native }).returns({ 'k1' => 'global_e1' })
209
- resources = assemble_and_compile('${r[k1]}_${r[k2]}_${r[k3]}', "'abc::e'", 'Hash[String,String]', "{strategy => 'hash'}")
210
- expect(resources).to include('global_e1_module_e2_env_e3')
211
- end
212
322
 
213
- it 'will propagate a Hash resolution_type with :behavior => :deeper to Hiera when merge == \'deep\'' do
214
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
215
- Hiera.any_instance.expects(:lookup).with('abc::f', anything, anything, anything, { :behavior => :deeper }).returns({ 'k1' => { 's1' => 'global_f11' }, 'k2' => { 's3' => 'global_f23' }})
216
- resources = assemble_and_compile('${r[k1][s1]}_${r[k1][s2]}_${r[k1][s3]}_${r[k2][s1]}_${r[k2][s2]}_${r[k2][s3]}', "'abc::f'", 'Hash[String,Hash[String,String]]', "'deep'")
217
- expect(resources).to include('global_f11_env_f12_module_f13_env_f21_module_f22_global_f23')
218
- end
323
+ context 'with lookup_options configured using patterns' do
324
+ let(:mod_common) {
325
+ <<-YAML.unindent
326
+ mod::hash_a:
327
+ aa:
328
+ aaa: aaa (from module)
329
+ ab:
330
+ aba: aba (from module)
331
+ mod::hash_b:
332
+ ba:
333
+ baa: baa (from module)
334
+ bb:
335
+ bba: bba (from module)
336
+ lookup_options:
337
+ '^mod::ha.*_a':
338
+ merge: deep
339
+ '^mod::ha.*_b':
340
+ merge: deep
341
+ YAML
342
+ }
219
343
 
220
- it 'will propagate a Hash resolution_type with symbolic deep merge options to Hiera' do
221
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
222
- Hiera.any_instance.expects(:lookup).with('abc::f', anything, anything, anything, { :behavior => :deeper, :knockout_prefix => '--' }).returns({ 'k1' => { 's1' => 'global_f11' }, 'k2' => { 's3' => 'global_f23' }})
223
- resources = assemble_and_compile('${r[k1][s1]}_${r[k1][s2]}_${r[k1][s3]}_${r[k2][s1]}_${r[k2][s2]}_${r[k2][s3]}', "'abc::f'", 'Hash[String,Hash[String,String]]', "{ 'strategy' => 'deep', 'knockout_prefix' => '--' }")
224
- expect(resources).to include('global_f11_env_f12_module_f13_env_f21_module_f22_global_f23')
225
- end
344
+ let(:mod_base) do
345
+ {
346
+ 'hiera.yaml' => <<-YAML.unindent,
347
+ version: 5
348
+ YAML
349
+ 'data' => {
350
+ 'common.yaml' => mod_common
351
+ }
352
+ }
353
+ end
226
354
 
227
- context 'with provided default' do
228
- it 'will return default when lookup fails' do
229
- resources = assemble_and_compile('${r}', "'abc::x'", 'String', 'undef', "'dflt_x'")
230
- expect(resources).to include('dflt_x')
355
+ let(:environment_files) do
356
+ {
357
+ env_name => {
358
+ 'hiera.yaml' => <<-YAML.unindent,
359
+ ---
360
+ version: 5
361
+ hierarchy:
362
+ - name: X
363
+ paths:
364
+ - first.yaml
365
+ - second.yaml
366
+ YAML
367
+ 'data' => {
368
+ 'first.yaml' => <<-YAML.unindent,
369
+ a:
370
+ aa:
371
+ aaa: a.aa.aaa
372
+ b:
373
+ ba:
374
+ baa: b.ba.baa
375
+ bb:
376
+ bba: b.bb.bba
377
+ c:
378
+ ca:
379
+ caa: c.ca.caa
380
+ mod::hash_a:
381
+ aa:
382
+ aab: aab (from environment)
383
+ ab:
384
+ aba: aba (from environment)
385
+ abb: abb (from environment)
386
+ mod::hash_b:
387
+ ba:
388
+ bab: bab (from environment)
389
+ bc:
390
+ bca: bca (from environment)
391
+ lookup_options:
392
+ b:
393
+ merge: hash
394
+ '^[^b]$':
395
+ merge: deep
396
+ '^c':
397
+ merge: first
398
+ '^b':
399
+ merge: first
400
+ '^mod::ha.*_b':
401
+ merge: hash
402
+ YAML
403
+ 'second.yaml' => <<-YAML.unindent,
404
+ a:
405
+ aa:
406
+ aab: a.aa.aab
407
+ b:
408
+ ba:
409
+ bab: b.ba.bab
410
+ bb:
411
+ bbb: b.bb.bbb
412
+ c:
413
+ ca:
414
+ cab: c.ca.cab
415
+ YAML
416
+ },
417
+ 'modules' => {
418
+ 'mod' => mod_base
419
+ }
420
+ }
421
+ }
231
422
  end
232
423
 
233
- it 'can precede default parameter with undef as the value_type and undef as the merge type' do
234
- resources = assemble_and_compile('${r}', "'abc::x'", 'undef', 'undef', "'dflt_x'")
235
- expect(resources).to include('dflt_x')
424
+ it 'finds lookup_options that matches a pattern' do
425
+ expect(lookup('a')).to eql({'aa' => { 'aaa' => 'a.aa.aaa', 'aab' => 'a.aa.aab' }})
236
426
  end
237
427
 
238
- it 'can use array' do
239
- resources = assemble_and_compile('${r[0]}_${r[1]}', "'abc::x'", 'Array[String]', 'undef', "['dflt_x', 'dflt_y']")
240
- expect(resources).to include('dflt_x_dflt_y')
428
+ it 'gives a direct key match higher priority than a matching pattern' do
429
+ expect(lookup('b')).to eql({'ba' => { 'baa' => 'b.ba.baa' }, 'bb' => { 'bba'=>'b.bb.bba' }})
241
430
  end
242
431
 
243
- it 'can use hash' do
244
- resources = assemble_and_compile('${r[a]}_${r[b]}', "'abc::x'", 'Hash[String,String]', 'undef', "{'a' => 'dflt_x', 'b' => 'dflt_y'}")
245
- expect(resources).to include('dflt_x_dflt_y')
432
+ it 'uses the first matching pattern' do
433
+ expect(lookup('c')).to eql({'ca' => { 'caa' => 'c.ca.caa', 'cab' => 'c.ca.cab' }})
246
434
  end
247
435
 
248
- it 'fails unless default is an instance of value_type' do
249
- expect do
250
- assemble_and_compile('${r[a]}_${r[b]}', "'abc::x'", 'Hash[String,String]', 'undef', "{'a' => 'dflt_x', 'b' => 32}")
251
- end.to raise_error(Puppet::ParseError,
252
- /Default value has wrong type, entry 'b' expects a String value, got Integer/)
436
+ it 'uses lookup_option found by pattern from module' do
437
+ expect(lookup('mod::hash_a')).to eql({
438
+ 'aa' => {
439
+ 'aaa' => 'aaa (from module)',
440
+ 'aab' => 'aab (from environment)'
441
+ },
442
+ 'ab' => {
443
+ 'aba' => 'aba (from environment)',
444
+ 'abb' => 'abb (from environment)'
445
+ }
446
+ })
447
+ end
448
+
449
+ it 'merges lookup_options found by pattern in environment and module (environment wins)' do
450
+ expect(lookup('mod::hash_b')).to eql({
451
+ 'ba' => {
452
+ 'bab' => 'bab (from environment)'
453
+ },
454
+ 'bb' => {
455
+ 'bba' => 'bba (from module)'
456
+ },
457
+ 'bc' => {
458
+ 'bca' => 'bca (from environment)'
459
+ }
460
+ })
461
+ end
462
+
463
+ context 'and patterns in module are not limited to module keys' do
464
+ let(:mod_common) {
465
+ <<-YAML.unindent
466
+ mod::hash_a:
467
+ aa:
468
+ aaa: aaa (from module)
469
+ ab:
470
+ aba: aba (from module)
471
+ lookup_options:
472
+ '^.*_a':
473
+ merge: deep
474
+ YAML
475
+ }
476
+
477
+ it 'fails with error' do
478
+ expect { lookup('mod::a') }.to raise_error(Puppet::DataBinding::LookupError, /all lookup_options patterns must match a key starting with module name/)
479
+ end
253
480
  end
254
481
  end
255
482
 
256
- context 'with a default block' do
257
- it 'will be called when lookup fails' do
258
- resources = assemble_and_compile_with_block('${r}', "'dflt_x'", "'abc::x'")
259
- expect(resources).to include('dflt_x')
483
+ context 'and a global Hiera v4 configuration' do
484
+ let(:code_dir_files) do
485
+ {
486
+ 'hiera.yaml' => <<-YAML.unindent,
487
+ ---
488
+ version: 4
489
+ YAML
490
+ }
260
491
  end
261
492
 
262
- it 'will not called when lookup succeeds but the found value is nil' do
263
- resources = assemble_and_compile_with_block('${r}', "'dflt_x'", "'abc::n'")
264
- expect(resources).to include('no_value')
493
+ before(:each) do
494
+ # Need to set here since spec_helper defines these settings in its "before each"
495
+ Puppet.settings[:codedir] = populated_code_dir
496
+ Puppet.settings[:hiera_config] = File.join(code_dir, 'hiera.yaml')
265
497
  end
266
498
 
267
- it 'can use array' do
268
- resources = assemble_and_compile_with_block('${r[0]}_${r[1]}', "['dflt_x', 'dflt_y']", "'abc::x'")
269
- expect(resources).to include('dflt_x_dflt_y')
499
+ it 'raises an error' do
500
+ expect { lookup('a') }.to raise_error(Puppet::Error, /hiera configuration version 4 cannot be used in the global layer/)
270
501
  end
502
+ end
271
503
 
272
- it 'can use hash' do
273
- resources = assemble_and_compile_with_block('${r[a]}_${r[b]}', "{'a' => 'dflt_x', 'b' => 'dflt_y'}", "'abc::x'")
274
- expect(resources).to include('dflt_x_dflt_y')
504
+ context 'and an environment Hiera v3 configuration' do
505
+ let(:environment_files) do
506
+ {
507
+ env_name => {
508
+ 'hiera.yaml' => <<-YAML.unindent,
509
+ ---
510
+ :backends: yaml
511
+ YAML
512
+ }
513
+ }
275
514
  end
276
515
 
277
- it 'can return undef from block' do
278
- resources = assemble_and_compile_with_block('${r}', 'undef', "'abc::x'")
279
- expect(resources).to include('no_value')
516
+ it 'raises an error' do
517
+ expect { lookup('a') }.to raise_error(Puppet::Error, /hiera configuration version 3 cannot be used in an environment/)
280
518
  end
519
+ end
281
520
 
282
- it 'fails unless block returns an instance of value_type' do
283
- expect do
284
- assemble_and_compile_with_block('${r[a]}_${r[b]}', "{'a' => 'dflt_x', 'b' => 32}", "'abc::x'", 'Hash[String,String]')
285
- end.to raise_error(Puppet::ParseError,
286
- /Value returned from default block has wrong type, entry 'b' expects a String value, got Integer/)
521
+ context 'and a global empty Hiera configuration' do
522
+ let(:hiera_yaml_path) { File.join(code_dir, 'hiera.yaml') }
523
+ let(:code_dir_files) do
524
+ {
525
+ 'hiera.yaml' => '',
526
+ }
287
527
  end
288
528
 
289
- it 'receives a single name parameter' do
290
- resources = assemble_and_compile_with_block('${r}', 'true', "'name_x'")
291
- expect(resources).to include('name_x')
529
+ let(:environment_files) do
530
+ {
531
+ env_name => {
532
+ 'hieradata' => {
533
+ 'common.yaml' => <<-YAML.unindent,
534
+ x: value x (from environment)
535
+ YAML
536
+ }
537
+ }
538
+ }
292
539
  end
293
540
 
294
- it 'receives an array name parameter' do
295
- resources = assemble_and_compile_with_block('${r[0]}_${r[1]}', 'true', "['name_x', 'name_y']")
296
- expect(resources).to include('name_x_name_y')
541
+ before(:each) do
542
+ # Need to set here since spec_helper defines these settings in its "before each"
543
+ Puppet.settings[:hiera_config] = hiera_yaml_path
297
544
  end
298
- end
299
- end
300
545
 
301
- context 'when using dotted keys' do
302
- it 'can access values in data using dot notation' do
303
- source = <<-CODE
304
- function environment::data() {
305
- { a => { b => { c => 'the data' }}}
306
- }
307
- notice(lookup('a.b.c'))
308
- CODE
309
- expect(eval_and_collect_notices(source)).to include('the data')
310
- end
546
+ it 'uses a Hiera version 3 defaults' do
547
+ expect(lookup('x')).to eql('value x (from environment)')
548
+ end
311
549
 
312
- it 'can find data using quoted dot notation' do
313
- source = <<-CODE
314
- function environment::data() {
315
- { 'a.b.c' => 'the data' }
316
- }
317
- notice(lookup('"a.b.c"'))
318
- CODE
319
- expect(eval_and_collect_notices(source)).to include('the data')
320
- end
550
+ context 'obtained using /dev/null', :unless => Puppet.features.microsoft_windows? do
551
+ let(:code_dir_files) { {} }
321
552
 
322
- it 'can access values in data using a mix of dot notation and quoted dot notation' do
323
- source = <<-CODE
324
- function environment::data() {
325
- { 'a' => { 'b.c' => 'the data' }}
326
- }
327
- notice(lookup('a."b.c"'))
328
- CODE
329
- expect(eval_and_collect_notices(source)).to include('the data')
553
+ it 'uses a Hiera version 3 defaults' do
554
+ Puppet[:hiera_config] = '/dev/null'
555
+ expect(lookup('x')).to eql('value x (from environment)')
556
+ end
557
+ end
330
558
  end
331
- end
332
559
 
333
- context 'when passing a hash as the only parameter' do
334
- it 'can pass a single name correctly' do
335
- resources = assemble_and_compile('${r}', "{name => 'abc::a'}")
336
- expect(resources).to include('env_a')
337
- end
560
+ context 'and a global configuration' do
561
+ let(:hiera_yaml) do
562
+ <<-YAML.unindent
563
+ ---
564
+ :backends:
565
+ - yaml
566
+ - json
567
+ - custom
568
+ - hocon
569
+ :yaml:
570
+ :datadir: #{code_dir}/hieradata
571
+ :json:
572
+ :datadir: #{code_dir}/hieradata
573
+ :hocon:
574
+ :datadir: #{code_dir}/hieradata
575
+ :hierarchy:
576
+ - common
577
+ - "%{domain}"
578
+ :merge_behavior: deeper
579
+ YAML
580
+ end
338
581
 
339
- it 'can pass a an array of names correctly' do
340
- resources = assemble_and_compile('${r}', "{name => ['abc::b', 'abc::a']}")
341
- expect(resources).to include('module_b')
342
- end
582
+ let(:code_dir_files) do
583
+ {
584
+ 'hiera.yaml' => hiera_yaml,
585
+ 'ruby_stuff' => {
586
+ 'hiera' => {
587
+ 'backend' => {
588
+ 'custom_backend.rb' => <<-RUBY.unindent,
589
+ class Hiera::Backend::Custom_backend
590
+ def lookup(key, scope, order_override, resolution_type, context)
591
+ case key
592
+ when 'hash_c'
593
+ { 'hash_ca' => { 'cad' => 'value hash_c.hash_ca.cad (from global custom)' }}
594
+ when 'datasources'
595
+ Hiera::Backend.datasources(scope, order_override) { |source| source }
596
+ else
597
+ throw :no_such_key
598
+ end
599
+ end
600
+ end
601
+ RUBY
602
+ 'other_backend.rb' => <<-RUBY.unindent,
603
+ class Hiera::Backend::Other_backend
604
+ def lookup(key, scope, order_override, resolution_type, context)
605
+ value = Hiera::Config[:other][key.to_sym]
606
+ throw :no_such_key if value.nil?
607
+ value
608
+ end
609
+ end
610
+ RUBY
611
+ }
612
+ }
613
+ },
614
+ 'hieradata' => {
615
+ 'common.yaml' => <<-YAML.unindent,
616
+ a: value a (from global)
617
+ hash_b:
618
+ hash_ba:
619
+ bab: value hash_b.hash_ba.bab (from global)
620
+ hash_c:
621
+ hash_ca:
622
+ cab: value hash_c.hash_ca.cab (from global)
623
+ YAML
624
+ 'example.com.yaml' => <<-YAML.unindent,
625
+ x: value x (from global example.com.yaml)
626
+ YAML
627
+ 'common.json' => <<-JSON.unindent,
628
+ {
629
+ "hash_b": {
630
+ "hash_ba": {
631
+ "bac": "value hash_b.hash_ba.bac (from global json)"
632
+ }
633
+ },
634
+ "hash_c": {
635
+ "hash_ca": {
636
+ "cac": "value hash_c.hash_ca.cac (from global json)"
637
+ }
638
+ }
639
+ }
640
+ JSON
641
+ 'common.conf' => <<-HOCON.unindent,
642
+ // The 'xs' is a value used for testing
643
+ xs = { subkey = value xs.subkey (from global hocon) }
644
+ HOCON
645
+ }
646
+ }
647
+ end
343
648
 
344
- it 'can pass an override map and find values there even though they would be found' do
345
- resources = assemble_and_compile('${r}', "{name => 'abc::a', override => { abc::a => 'override_a'}}")
346
- expect(resources).to include('override_a')
347
- end
649
+ before(:each) do
650
+ # Need to set here since spec_helper defines these settings in its "before each"
651
+ Puppet.settings[:codedir] = populated_code_dir
652
+ Puppet.settings[:hiera_config] = File.join(code_dir, 'hiera.yaml')
653
+ end
348
654
 
349
- it 'can pass an default_values_hash and find values there correctly' do
350
- resources = assemble_and_compile('${r}', "{name => 'abc::x', default_values_hash => { abc::x => 'extra_x'}}")
351
- expect(resources).to include('extra_x')
352
- end
655
+ around(:each) do |example|
656
+ # Faking the load path to enable 'require' to load from 'ruby_stuff'. It removes the need for a static fixture
657
+ # for the custom backend
658
+ $LOAD_PATH.unshift(File.join(code_dir, 'ruby_stuff'))
659
+ begin
660
+ Puppet.override(:environments => environments, :current_environment => env) do
661
+ example.run
662
+ end
663
+ ensure
664
+ Hiera::Backend.send(:remove_const, :Custom_backend) if Hiera::Backend.const_defined?(:Custom_backend)
665
+ Hiera::Backend.send(:remove_const, :Other_backend) if Hiera::Backend.const_defined?(:Other_backend)
666
+ $LOAD_PATH.shift
667
+ end
668
+ end
353
669
 
354
- it 'can pass an default_values_hash but not use it when value is found elsewhere' do
355
- resources = assemble_and_compile('${r}', "{name => 'abc::a', default_values_hash => { abc::a => 'extra_a'}}")
356
- expect(resources).to include('env_a')
357
- end
670
+ context 'version 3' do
671
+ it 'finds data in the environment and reports deprecation warnings for both environment.conf and hiera.yaml' do
672
+ expect(lookup('a')).to eql('value a (from global)')
673
+ expect(warnings).to include(/Use of 'hiera.yaml' version 3 is deprecated. It should be converted to version 5/)
674
+ end
358
675
 
359
- it 'can pass an default_values_hash but not use it when value is found elsewhere even when found value is undef' do
360
- resources = assemble_and_compile('${r}', "{name => 'abc::n', default_values_hash => { abc::n => 'extra_n'}}")
361
- expect(resources).to include('no_value')
362
- end
676
+ it 'explain contains output from global layer' do
677
+ explanation = explain('a')
678
+ expect(explanation).to include('Global Data Provider (hiera configuration version 3)')
679
+ expect(explanation).to include('Hierarchy entry "yaml"')
680
+ expect(explanation).to include('Hierarchy entry "json"')
681
+ expect(explanation).to include('Found key: "a" value: "value a (from global)"')
682
+ end
363
683
 
364
- it 'can pass an override and an default_values_hash and find the override value' do
365
- resources = assemble_and_compile('${r}', "{name => 'abc::x', override => { abc::x => 'override_x'}, default_values_hash => { abc::x => 'extra_x'}}")
366
- expect(resources).to include('override_x')
367
- end
684
+ it 'uses the merge behavior specified in global hiera.yaml to merge only global backends' do
685
+ expect(lookup('hash_b')).to eql(
686
+ { 'hash_ba' => { 'bab' => 'value hash_b.hash_ba.bab (from global)', 'bac' => 'value hash_b.hash_ba.bac (from global json)' } })
687
+ end
368
688
 
369
- it 'will raise an exception when value is not found for single key and no default is provided' do
370
- expect do
371
- assemble_and_compile('${r}', "{name => 'abc::x'}")
372
- end.to raise_error(Puppet::ParseError, /did not find a value for the name 'abc::x'/)
373
- end
689
+ it 'uses the merge from lookup options to merge all layers and override merge_behavior specified in global hiera.yaml' do
690
+ expect(lookup('hash_c')).to eql(
691
+ { 'hash_ca' => { 'cab' => 'value hash_c.hash_ca.cab (from global)' } })
692
+ end
374
693
 
375
- it 'will not raise an exception when value is not found default value is nil' do
376
- resources = assemble_and_compile('${r}', "{name => 'abc::x', default_value => undef}")
377
- expect(resources).to include('no_value')
378
- end
379
- end
694
+ it 'uses the explicitly given merge to override lookup options and to merge all layers' do
695
+ expect(lookup('hash_c', 'merge' => 'deep')).to eql(
696
+ {
697
+ 'hash_ca' =>
698
+ {
699
+ 'caa' => 'value hash_c.hash_ca.caa (from environment)',
700
+ 'cab' => 'value hash_c.hash_ca.cab (from global)',
701
+ 'cac' => 'value hash_c.hash_ca.cac (from global json)',
702
+ 'cad' => 'value hash_c.hash_ca.cad (from global custom)'
703
+ }
704
+ })
705
+ end
380
706
 
381
- context 'when accessing from outside a module' do
382
- it 'will both log a warning and raise an exception when key in the function provided module data is not prefixed' do
383
- logs = []
384
- Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
385
- Puppet[:code] = "include bad_data\nlookup('bad_data::b')"
386
- expect { compiler.compile }.to raise_error(Puppet::ParseError, /did not find a value for the name 'bad_data::b'/)
387
- end
388
- warnings = logs.select {|log| log.level == :warning }.map {|log| log.message }
389
- expect(warnings).to include("Module data for module 'bad_data' must use keys qualified with the name of the module")
390
- end
707
+ it 'paths are interpolated' do
708
+ expect(lookup('x')).to eql('value x (from global example.com.yaml)')
709
+ end
391
710
 
392
- it 'will succeed finding prefixed keys even when a key in the function provided module data is not prefixed' do
393
- logs = []
394
- resources = nil
395
- Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
396
- resources = compile_and_get_notifications(<<-END.gsub(/^ {10}/, ''))
397
- include bad_data
398
- notify { lookup('bad_data::c'): }
399
- END
400
- expect(resources).to include('module_c')
401
- end
402
- warnings = logs.select {|log| log.level == :warning }.map {|log| log.message }
403
- expect(warnings).to include("Module data for module 'bad_data' must use keys qualified with the name of the module")
404
- end
711
+ it 'backend data sources are propagated to custom backend' do
712
+ expect(lookup('datasources')).to eql(['common', 'example.com'])
713
+ end
405
714
 
406
- it 'will resolve global, environment, and module correctly' do
407
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
408
- Hiera.any_instance.expects(:lookup).with('bca::e', any_parameters).returns({ 'k1' => 'global_e1' })
409
- resources = compile_and_get_notifications(<<-END.gsub(/^ {8}/, '')
410
- include bca
411
- $r = lookup(bca::e, Hash[String,String], hash)
412
- notify { "${r[k1]}_${r[k2]}_${r[k3]}": }
413
- END
414
- )
415
- expect(resources).to include('global_e1_module_bca_e2_env_bca_e3')
416
- end
715
+ it 'delegates configured hocon backend to hocon_data function' do
716
+ expect(explain('xs')).to match(/Hierarchy entry "hocon"\n.*\n.*\n.*"common"\n\s*Found key: "xs"/m)
717
+ end
417
718
 
418
- it 'will resolve global and environment correctly when module has no provider' do
419
- Hiera.any_instance.expects(:lookup).with('lookup_options', any_parameters).at_most_once.throws(:no_such_key)
420
- Hiera.any_instance.expects(:lookup).with('no_provider::e', any_parameters).returns({ 'k1' => 'global_e1' })
421
- resources = compile_and_get_notifications(<<-END.gsub(/^ {8}/, '')
422
- include no_provider
423
- $r = lookup(no_provider::e, Hash[String,String], hash)
424
- notify { "${r[k1]}_${r[k2]}_${r[k3]}": }
425
- END
426
- )
427
- expect(resources).to include('global_e1__env_no_provider_e3') # k2 is missing
428
- end
429
- end
719
+ it 'can dig down into subkeys provided by hocon_data function' do
720
+ expect(lookup('xs.subkey')).to eql('value xs.subkey (from global hocon)')
721
+ end
430
722
 
431
- context 'when accessing bad data' do
432
- it 'a warning will be logged when key in the function provided module data is not prefixed' do
433
- Puppet[:code] = "include bad_data\nlookup('bad_data::c')"
434
- logs = []
435
- Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
436
- compiler.compile
437
- end
438
- warnings = logs.select {|log| log.level == :warning }.map {|log| log.message }
439
- expect(warnings).to include("Module data for module 'bad_data' must use keys qualified with the name of the module")
440
- end
723
+ context 'using deep_merge_options supported by deep_merge gem but not supported by Puppet' do
724
+
725
+ let(:hiera_yaml) do
726
+ <<-YAML.unindent
727
+ ---
728
+ :backends:
729
+ - yaml
730
+ :yaml:
731
+ :datadir: #{code_dir}/hieradata
732
+ :hierarchy:
733
+ - other
734
+ - common
735
+ :merge_behavior: deeper
736
+ :deep_merge_options:
737
+ :unpack_arrays: ','
738
+ YAML
739
+ end
740
+
741
+ let(:code_dir_files) do
742
+ {
743
+ 'hiera.yaml' => hiera_yaml,
744
+ 'hieradata' => {
745
+ 'common.yaml' => <<-YAML.unindent,
746
+ a:
747
+ - x1,x2
748
+ YAML
749
+ 'other.yaml' => <<-YAML.unindent,
750
+ a:
751
+ - x3
752
+ - x4
753
+ YAML
754
+ }
755
+ }
756
+ end
441
757
 
442
- it 'a warning will be logged when key in the hiera provided module data is not prefixed' do
443
- Puppet[:code] = "include hieraprovider\nlookup('hieraprovider::test::param_a')"
444
- logs = []
445
- Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
446
- compiler.compile
758
+ it 'honors option :unpack_arrays: (unsupported by puppet)' do
759
+ expect(lookup('a')).to eql(%w(x1 x2 x3 x4))
760
+ end
761
+ end
762
+
763
+ context 'using relative datadir paths' do
764
+ let(:hiera_yaml) do
765
+ <<-YAML.unindent
766
+ ---
767
+ :backends:
768
+ - yaml
769
+ :yaml:
770
+ :datadir: relative_data
771
+ :hierarchy:
772
+ - common
773
+ YAML
774
+ end
775
+
776
+ let(:populated_code_dir) do
777
+ dir_contained_in(code_dir, code_dir_files.merge({
778
+ 'fake_cwd' => {
779
+ 'relative_data' => {
780
+ 'common.yaml' => <<-YAML.unindent
781
+ a: value a (from fake_cwd/relative_data/common.yaml)
782
+ YAML
783
+ }
784
+ }
785
+ }))
786
+ code_dir
787
+ end
788
+
789
+ around(:each) do |example|
790
+ cwd = Dir.pwd
791
+ Dir.chdir(File.join(code_dir, 'fake_cwd'))
792
+ begin
793
+ example.run
794
+ ensure
795
+ Dir.chdir(cwd)
796
+ end
797
+ end
798
+
799
+ it 'finds data from data file beneath relative datadir' do
800
+ expect(lookup('a')).to eql('value a (from fake_cwd/relative_data/common.yaml)')
801
+ end
802
+ end
447
803
  end
448
- warnings = logs.select {|log| log.level == :warning }.map {|log| log.message }
449
- expect(warnings).to include("Module data for module 'hieraprovider' must use keys qualified with the name of the module")
450
- end
451
- end
452
804
 
453
- context 'when accessing empty files' do
454
- # An empty YAML file is OK and should be treated as a file that contains no keys
455
- it "will fail normally with a 'did not find a value' error when a yaml file is empty" do
456
- Puppet[:code] = "include empty_yaml\nlookup('empty_yaml::a')"
457
- expect { compiler.compile }.to raise_error(Puppet::ParseError, /did not find a value for the name 'empty_yaml::a'/)
458
- end
805
+ context 'version 5' do
806
+ let(:hiera_yaml) do
807
+ <<-YAML.unindent
808
+ ---
809
+ version: 5
810
+ defaults:
811
+ datadir: hieradata
812
+
813
+ hierarchy:
814
+ - name: Yaml
815
+ data_hash: yaml_data
816
+ paths:
817
+ - common.yaml
818
+ - "%{domain}.yaml"
819
+ - name: Json
820
+ data_hash: json_data
821
+ paths:
822
+ - common.json
823
+ - "%{domain}.json"
824
+ - name: Hocon
825
+ data_hash: hocon_data
826
+ paths:
827
+ - common.conf
828
+ - "%{domain}.conf"
829
+ - name: Custom
830
+ hiera3_backend: custom
831
+ paths:
832
+ - common.custom
833
+ - "%{domain}.custom"
834
+ - name: Other
835
+ hiera3_backend: other
836
+ options:
837
+ other_option: value of other_option
838
+ paths:
839
+ - common.other
840
+ - "%{domain}.other"
841
+ YAML
842
+ end
459
843
 
460
- # An empty JSON file is not OK. Should yield a parse error
461
- it "will fail with a LookupError indicating a parser failure when a json file is empty" do
462
- Puppet[:code] = "include empty_json\nlookup('empty_json::a')"
463
- expect { compiler.compile }.to raise_error(Puppet::DataBinding::LookupError, /Unable to parse/)
464
- end
465
- end
844
+ it 'finds data in the environment and reports no deprecation warnings' do
845
+ expect(lookup('a')).to eql('value a (from global)')
846
+ expect(warnings).to be_empty
847
+ end
466
848
 
467
- context 'when accessing nil values' do
468
- it 'will find a key with undef value in a yaml file' do
469
- Puppet[:code] = 'include empty_key_yaml'
470
- compiler.compile do |catalog|
471
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)
472
- begin
473
- Puppet::Pops::Lookup.lookup('empty_key_yaml::has_undef_value',nil, nil, false, nil, lookup_invocation)
474
- rescue Puppet::Error
475
- end
476
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
477
- Merge strategy first
478
- Data Binding "hiera"
479
- No such key: "empty_key_yaml::has_undef_value"
480
- Data Provider "FunctionEnvDataProvider"
481
- No such key: "empty_key_yaml::has_undef_value"
482
- Module "empty_key_yaml" using Data Provider "Hiera Data Provider, version 4"
483
- ConfigurationPath "#{environmentpath}/production/modules/empty_key_yaml/hiera.yaml"
484
- Data Provider "empty_key"
485
- Path "#{environmentpath}/production/modules/empty_key_yaml/data/empty_key.yaml"
486
- Original path: "empty_key"
487
- Found key: "empty_key_yaml::has_undef_value" value: nil
488
- Merged result: nil
489
- EOS
490
- end
491
- end
849
+ it 'explain contains output from global layer' do
850
+ explanation = explain('a')
851
+ expect(explanation).to include('Global Data Provider (hiera configuration version 5)')
852
+ expect(explanation).to include('Hierarchy entry "Yaml"')
853
+ expect(explanation).to include('Hierarchy entry "Json"')
854
+ expect(explanation).to include('Hierarchy entry "Hocon"')
855
+ expect(explanation).to include('Hierarchy entry "Custom"')
856
+ expect(explanation).to include('Found key: "a" value: "value a (from global)"')
857
+ end
492
858
 
493
- it 'will find a key with undef value in a json file' do
494
- Puppet[:code] = 'include empty_key_json'
495
- compiler.compile do |catalog|
496
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)
497
- begin
498
- Puppet::Pops::Lookup.lookup('empty_key_json::has_undef_value',nil, nil, false, nil, lookup_invocation)
499
- rescue Puppet::Error
500
- end
501
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
502
- Merge strategy first
503
- Data Binding "hiera"
504
- No such key: "empty_key_json::has_undef_value"
505
- Data Provider "FunctionEnvDataProvider"
506
- No such key: "empty_key_json::has_undef_value"
507
- Module "empty_key_json" using Data Provider "Hiera Data Provider, version 4"
508
- ConfigurationPath "#{environmentpath}/production/modules/empty_key_json/hiera.yaml"
509
- Data Provider "empty_key"
510
- Path "#{environmentpath}/production/modules/empty_key_json/data/empty_key.json"
511
- Original path: "empty_key"
512
- Found key: "empty_key_json::has_undef_value" value: nil
513
- Merged result: nil
514
- EOS
515
- end
516
- end
517
- end
859
+ it 'uses the explicitly given merge to override lookup options and to merge all layers' do
860
+ expect(lookup('hash_c', 'merge' => 'deep')).to eql(
861
+ {
862
+ 'hash_ca' =>
863
+ {
864
+ 'caa' => 'value hash_c.hash_ca.caa (from environment)',
865
+ 'cab' => 'value hash_c.hash_ca.cab (from global)',
866
+ 'cac' => 'value hash_c.hash_ca.cac (from global json)',
867
+ 'cad' => 'value hash_c.hash_ca.cad (from global custom)'
868
+ }
869
+ })
870
+ end
518
871
 
519
- context 'when using explain' do
520
- it 'will explain that module is not found' do
521
- assemble_and_compile('${r}', "'abc::a'") do |scope|
522
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
523
- begin
524
- Puppet::Pops::Lookup.lookup('ppx::e',nil, nil, false, nil, lookup_invocation)
525
- rescue Puppet::Error
526
- end
527
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
528
- Merge strategy first
529
- Data Binding "hiera"
530
- No such key: "ppx::e"
531
- Data Provider "FunctionEnvDataProvider"
532
- No such key: "ppx::e"
533
- Module "ppx"
534
- Module not found
535
- EOS
536
- end
537
- end
872
+ it 'backend data sources are propagated to custom backend' do
873
+ expect(lookup('datasources')).to eql(['common', 'example.com'])
874
+ end
538
875
 
539
- it 'will explain that module does not find a key' do
540
- assemble_and_compile('${r}', "'abc::a'") do |scope|
541
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
542
- begin
543
- Puppet::Pops::Lookup.lookup('abc::x', nil, nil, false, nil, lookup_invocation)
544
- rescue Puppet::Error
545
- end
546
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
547
- Merge strategy first
548
- Data Binding "hiera"
549
- No such key: "abc::x"
550
- Data Provider "FunctionEnvDataProvider"
551
- No such key: "abc::x"
552
- Module "abc" using Data Provider "FunctionModuleDataProvider"
553
- No such key: "abc::x"
554
- EOS
555
- end
556
- end
876
+ it 'backend specific options are propagated to custom backend' do
877
+ expect(lookup('other_option')).to eql('value of other_option')
878
+ end
557
879
 
558
- it 'will explain deep merge results without options' do
559
- assemble_and_compile('${r}', "'abc::a'") do |scope|
560
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
561
- Puppet::Pops::Lookup.lookup('abc::e', Puppet::Pops::Types::TypeParser.singleton.parse('Hash[String,String]'), nil, false, 'deep', lookup_invocation)
562
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
563
- Merge strategy deep
564
- Data Binding "hiera"
565
- No such key: "abc::e"
566
- Data Provider "FunctionEnvDataProvider"
567
- Found key: "abc::e" value: {
568
- "k1" => "env_e1",
569
- "k3" => "env_e3"
570
- }
571
- Module "abc" using Data Provider "FunctionModuleDataProvider"
572
- Found key: "abc::e" value: {
573
- "k1" => "module_e1",
574
- "k2" => "module_e2"
575
- }
576
- Merged result: {
577
- "k1" => "env_e1",
578
- "k2" => "module_e2",
579
- "k3" => "env_e3"
580
- }
581
- EOS
582
- end
583
- end
880
+ it 'multiple hiera3_backend declarations can be used and are merged into the generated config' do
881
+ expect(lookup(['datasources', 'other_option'])).to eql([['common', 'example.com'], 'value of other_option'])
882
+ expect(Hiera::Config.instance_variable_get(:@config)).to eql(
883
+ {
884
+ :backends => ['custom', 'other'],
885
+ :hierarchy => ['common', '%{domain}'],
886
+ :custom => { :datadir => "#{code_dir}/hieradata" },
887
+ :other => { :other_option => 'value of other_option', :datadir=>"#{code_dir}/hieradata" },
888
+ :logger => 'puppet'
889
+ })
890
+ end
891
+
892
+ it 'provides a sensible error message when the hocon library is not loaded' do
893
+ Puppet.features.stubs(:hocon?).returns(false)
584
894
 
585
- it 'will explain deep merge results with options' do
586
- assemble_and_compile('${r}', "'abc::a'") do |scope|
587
- Hiera.any_instance.expects(:lookup).with(any_parameters).returns({'k1' => 'global_g1'})
588
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
589
- Puppet::Pops::Lookup.lookup('abc::e', Puppet::Pops::Types::TypeParser.singleton.parse('Hash[String,String]'), nil, false, {'strategy' => 'deep', 'merge_hash_arrays' => true}, lookup_invocation)
590
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
591
- Merge strategy deep
592
- Options: {
593
- "merge_hash_arrays" => true
594
- }
595
- Data Binding "hiera"
596
- Found key: "abc::e" value: {
597
- "k1" => "global_g1"
598
- }
599
- Data Provider "FunctionEnvDataProvider"
600
- Found key: "abc::e" value: {
601
- "k1" => "env_e1",
602
- "k3" => "env_e3"
603
- }
604
- Module "abc" using Data Provider "FunctionModuleDataProvider"
605
- Found key: "abc::e" value: {
606
- "k1" => "module_e1",
607
- "k2" => "module_e2"
608
- }
609
- Merged result: {
610
- "k1" => "global_g1",
611
- "k2" => "module_e2",
612
- "k3" => "env_e3"
613
- }
614
- EOS
895
+ expect { lookup('a') }.to raise_error do |e|
896
+ expect(e.message).to match(/Lookup using Hocon data_hash function is not supported without hocon library/)
897
+ end
898
+ end
615
899
  end
616
- end
617
900
 
618
- it 'will handle path merge when some entries are not found correctly' do
619
- assemble_and_compile('${r}', "'hieraprovider::test::param_a'") do |scope|
620
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
621
- begin
622
- Puppet::Pops::Lookup.lookup('hieraprovider::test::not_found', nil, nil, false, nil, lookup_invocation)
623
- rescue Puppet::DataBinding::LookupError
624
- end
625
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
626
- Merge strategy first
627
- Data Binding "hiera"
628
- No such key: "hieraprovider::test::not_found"
629
- Data Provider "FunctionEnvDataProvider"
630
- No such key: "hieraprovider::test::not_found"
631
- Module "hieraprovider" using Data Provider "Hiera Data Provider, version 4"
632
- ConfigurationPath "#{environmentpath}/production/modules/hieraprovider/hiera.yaml"
633
- Data Provider "two paths"
634
- Merge strategy first
635
- Path "#{environmentpath}/production/modules/hieraprovider/data/first.json"
636
- Original path: "first"
637
- No such key: "hieraprovider::test::not_found"
638
- Path "#{environmentpath}/production/modules/hieraprovider/data/second_not_present.json"
639
- Original path: "second_not_present"
640
- Path not found
641
- EOS
901
+ context 'with a hiera3_backend that has no paths' do
902
+ let(:hiera_yaml) do
903
+ <<-YAML.unindent
904
+ ---
905
+ version: 5
906
+ hierarchy:
907
+ - name: Custom
908
+ hiera3_backend: custom
909
+ YAML
910
+ end
911
+
912
+ it 'calls the backend' do
913
+ expect(lookup('hash_c')).to eql(
914
+ { 'hash_ca' => { 'cad' => 'value hash_c.hash_ca.cad (from global custom)' }})
915
+ end
642
916
  end
643
917
  end
644
918
 
645
- it 'will explain value access caused by dot notation in key' do
646
- assemble_and_compile('${r}', "'abc::a'") do |scope|
647
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
648
- Puppet::Pops::Lookup.lookup('abc::f.k1.s1', Puppet::Pops::Types::TypeParser.singleton.parse('String'), nil, false, nil, lookup_invocation)
649
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
650
- Merge strategy first
651
- Data Binding "hiera"
652
- No such key: "abc::f.k1.s1"
653
- Data Provider "FunctionEnvDataProvider"
654
- Sub key: "k1.s1"
655
- Found key: "k1" value: {
656
- "s1" => "env_f11",
657
- "s2" => "env_f12"
658
- }
659
- Found key: "s1" value: "env_f11"
660
- Found key: "abc::f.k1.s1" value: "env_f11"
661
- Merged result: "env_f11"
662
- EOS
919
+ context 'and a module' do
920
+ let(:mod_a_files) { {} }
921
+
922
+ let(:populated_env_dir) do
923
+ dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files }))
924
+ env_dir
663
925
  end
664
- end
665
926
 
927
+ context 'that has no lookup configured' do
928
+ let(:mod_a_files) do
929
+ {
930
+ 'mod_a' => {
931
+ 'data' => {
932
+ 'common.yaml' => <<-YAML.unindent
933
+ ---
934
+ mod_a::b: value mod_a::b (from mod_a)
935
+ YAML
936
+ }
937
+ }
938
+ }
939
+ end
940
+
941
+ it 'does not find data in the module' do
942
+ expect { lookup('mod_a::b') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value for the name 'mod_a::b'/)
943
+ end
666
944
 
667
- it 'will provide a hash containing all explanation elements' do
668
- assemble_and_compile('${r}', "'abc::a'") do |scope|
669
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
670
- Puppet::Pops::Lookup.lookup('abc::e', Puppet::Pops::Types::TypeParser.singleton.parse('Hash[String,String]'), nil, false, {'strategy' => 'deep', 'merge_hash_arrays' => true}, lookup_invocation)
671
- expect(lookup_invocation.explainer.to_hash).to eq(
945
+ context 'with a Hiera v3 configuration' do
946
+ let(:mod_a_files) do
672
947
  {
673
- :branches => [
948
+ 'mod_a' => {
949
+ 'hiera.yaml' => <<-YAML.unindent
950
+ ---
951
+ :backends: yaml
952
+ YAML
953
+ }
954
+ }
955
+ end
956
+
957
+ it 'raises an error' do
958
+ expect { lookup('mod_a::a') }.to raise_error(Puppet::Error, /hiera configuration version 3 cannot be used in a module/)
959
+ end
960
+ end
961
+
962
+ context "but a metadata.json with 'module_data_provider=hiera'" do
963
+ let(:mod_a_files_1) { DeepMerge.deep_merge!(mod_a_files, 'mod_a' => { 'metadata.json' => <<-JSON.unindent }) }
674
964
  {
675
- :key => 'abc::e',
676
- :event => :not_found,
677
- :type => :global,
678
- :name => :hiera
965
+ "name": "example/mod_a",
966
+ "version": "0.0.2",
967
+ "source": "git@github.com/example/mod_a.git",
968
+ "dependencies": [],
969
+ "author": "Bob the Builder",
970
+ "license": "Apache-2.0",
971
+ "data_provider": "hiera"
972
+ }
973
+ JSON
974
+
975
+ let(:populated_env_dir) do
976
+ dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files_1 }))
977
+ env_dir
978
+ end
979
+
980
+ it 'finds data in the module and reports deprecation warning for metadata.json' do
981
+ expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
982
+ expect(warnings).to include(/Defining "data_provider": "hiera" in metadata.json is deprecated. A 'hiera.yaml' file should be used instead/)
983
+ end
984
+
985
+ context 'and a hiera.yaml file' do
986
+ let(:mod_a_files_2) { DeepMerge.deep_merge!(mod_a_files_1, 'mod_a' => { 'hiera.yaml' => <<-YAML.unindent }) }
987
+ ---
988
+ version: 4
989
+ hierarchy:
990
+ - name: common
991
+ backend: yaml
992
+ YAML
993
+
994
+ let(:populated_env_dir) do
995
+ dir_contained_in(env_dir, DeepMerge.deep_merge!(environment_files, env_name => { 'modules' => mod_a_files_2 }))
996
+ env_dir
997
+ end
998
+
999
+ it 'finds data in the module and reports deprecation warnings for both metadata.json and hiera.yaml' do
1000
+ expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
1001
+ expect(warnings).to include(/Defining "data_provider": "hiera" in metadata.json is deprecated/)
1002
+ expect(warnings).to include(/Use of 'hiera.yaml' version 4 is deprecated. It should be converted to version 5/)
1003
+ end
1004
+ end
1005
+ end
1006
+ end
1007
+
1008
+ context 'using a data_hash that reads a yaml file' do
1009
+ let(:mod_a_files) do
1010
+ {
1011
+ 'mod_a' => {
1012
+ 'data' => {
1013
+ 'common.yaml' => <<-YAML.unindent
1014
+ ---
1015
+ mod_a::a: value mod_a::a (from mod_a)
1016
+ mod_a::b: value mod_a::b (from mod_a)
1017
+ mod_a::xo: value mod_a::xo (from mod_a)
1018
+ mod_a::xd_found: value mod_a::xd_found (from mod_a)
1019
+ mod_a::interpolate_xo: "-- %{lookup('mod_a::xo')} --"
1020
+ mod_a::interpolate_xd: "-- %{lookup('mod_a::xd')} --"
1021
+ mod_a::interpolate_scope_xo: "-- %{scope_xo} --"
1022
+ mod_a::interpolate_scope_xd: "-- %{scope_xd} --"
1023
+ mod_a::hash_a:
1024
+ a: value mod_a::hash_a.a (from mod_a)
1025
+ b: value mod_a::hash_a.b (from mod_a)
1026
+ mod_a::hash_b:
1027
+ a: value mod_a::hash_b.a (from mod_a)
1028
+ b: value mod_a::hash_b.b (from mod_a)
1029
+ mod_a::interpolated: "-- %{lookup('mod_a::a')} --"
1030
+ mod_a::a_a: "-- %{lookup('mod_a::hash_a.a')} --"
1031
+ mod_a::a_b: "-- %{lookup('mod_a::hash_a.b')} --"
1032
+ mod_a::b_a: "-- %{lookup('mod_a::hash_b.a')} --"
1033
+ mod_a::b_b: "-- %{lookup('mod_a::hash_b.b')} --"
1034
+ mod_a::interpolate_array:
1035
+ - "-- %{lookup('mod_a::a')} --"
1036
+ - "-- %{lookup('mod_a::b')} --"
1037
+ mod_a::interpolate_literal: "-- %{literal('hello')} --"
1038
+ mod_a::interpolate_scope: "-- %{scope_scalar} --"
1039
+ mod_a::interpolate_scope_not_found: "-- %{scope_nope} --"
1040
+ mod_a::interpolate_scope_dig: "-- %{scope_hash.a} --"
1041
+ mod_a::interpolate_scope_dig_not_found: "-- %{scope_hash.nope} --"
1042
+ mod_a::quoted_interpolation: '-- %{lookup(''"mod_a::a.quoted.key"'')} --'
1043
+ "mod_a::a.quoted.key": "value mod_a::a.quoted.key (from mod_a)"
1044
+ YAML
679
1045
  },
680
- {
681
- :key => 'abc::e',
682
- :value => { 'k1' => 'env_e1', 'k3' => 'env_e3' },
683
- :event => :found,
684
- :type => :data_provider,
685
- :name => 'FunctionEnvDataProvider'
1046
+ 'hiera.yaml' => <<-YAML.unindent,
1047
+ ---
1048
+ version: 5
1049
+ hierarchy:
1050
+ - name: "Common"
1051
+ data_hash: yaml_data
1052
+ path: "common.yaml"
1053
+ YAML
1054
+ }
1055
+ }
1056
+ end
1057
+
1058
+ it 'finds data in the module' do
1059
+ expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
1060
+ end
1061
+
1062
+ it 'environment data has higher priority than module data' do
1063
+ expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')
1064
+ end
1065
+
1066
+ it 'environment data has higher priority than module data in interpolated module data' do
1067
+ expect(lookup('mod_a::interpolated')).to eql('-- value mod_a::a (from environment) --')
1068
+ end
1069
+
1070
+ it 'overrides have higher priority than found data' do
1071
+ expect(lookup('mod_a::xo', { 'override' => overrides })).to eql('value mod_a::xo (from override)')
1072
+ end
1073
+
1074
+ it 'overrides have higher priority than found data in lookup interpolations' do
1075
+ expect(lookup('mod_a::interpolate_xo', { 'override' => overrides })).to eql('-- value mod_a::xo (from override) --')
1076
+ end
1077
+
1078
+ it 'overrides have higher priority than found data in scope interpolations' do
1079
+ expect(lookup('mod_a::interpolate_scope_xo', { 'override' => overrides })).to eql('-- value scope_xo (from override) --')
1080
+ end
1081
+
1082
+ it 'defaults have lower priority than found data' do
1083
+ expect(lookup('mod_a::xd_found', { 'default_values_hash' => defaults })).to eql('value mod_a::xd_found (from mod_a)')
1084
+ end
1085
+
1086
+ it 'defaults are used when data is not found' do
1087
+ expect(lookup('mod_a::xd', { 'default_values_hash' => defaults })).to eql('value mod_a::xd (from default)')
1088
+ end
1089
+
1090
+ it 'defaults are used when data is not found in lookup interpolations' do
1091
+ expect(lookup('mod_a::interpolate_xd', { 'default_values_hash' => defaults })).to eql('-- value mod_a::xd (from default) --')
1092
+ end
1093
+
1094
+ it 'defaults are used when data is not found in scope interpolations' do
1095
+ expect(lookup('mod_a::interpolate_scope_xd', { 'default_values_hash' => defaults })).to eql('-- value scope_xd (from default) --')
1096
+ end
1097
+
1098
+ it 'merges hashes from environment and module unless strategy hash is used' do
1099
+ expect(lookup('mod_a::hash_a')).to eql({'a' => 'value mod_a::hash_a.a (from environment)'})
1100
+ end
1101
+
1102
+ it 'merges hashes from environment and module when merge strategy hash is used' do
1103
+ expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql(
1104
+ {'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)'})
1105
+ end
1106
+
1107
+ it 'will not merge hashes from environment and module in interpolated expressions' do
1108
+ expect(lookup(['mod_a::a_a', 'mod_a::a_b'])).to eql(
1109
+ ['-- value mod_a::hash_a.a (from environment) --', '-- --']) # root key found in environment, no hash merge is performed
1110
+ end
1111
+
1112
+ it 'interpolates arrays' do
1113
+ expect(lookup('mod_a::interpolate_array')).to eql(['-- value mod_a::a (from environment) --', '-- value mod_a::b (from mod_a) --'])
1114
+ end
1115
+
1116
+ it 'can dig into arrays using subkeys' do
1117
+ expect(lookup('mod_a::interpolate_array.1')).to eql('-- value mod_a::b (from mod_a) --')
1118
+ end
1119
+
1120
+ it 'treats an out of range subkey as not found' do
1121
+ expect(explain('mod_a::interpolate_array.2')).to match(/No such key: "2"/)
1122
+ end
1123
+
1124
+ it 'interpolates a literal' do
1125
+ expect(lookup('mod_a::interpolate_literal')).to eql('-- hello --')
1126
+ end
1127
+
1128
+ it 'interpolates scalar from scope' do
1129
+ expect(lookup('mod_a::interpolate_scope')).to eql('-- scope scalar value --')
1130
+ end
1131
+
1132
+ it 'interpolates not found in scope as empty string' do
1133
+ expect(lookup('mod_a::interpolate_scope_not_found')).to eql('-- --')
1134
+ end
1135
+
1136
+ it 'interpolates dotted key from scope' do
1137
+ expect(lookup('mod_a::interpolate_scope_dig')).to eql('-- scope hash a --')
1138
+ end
1139
+
1140
+ it 'treates interpolated dotted key but not found in scope as empty string' do
1141
+ expect(lookup('mod_a::interpolate_scope_dig_not_found')).to eql('-- --')
1142
+ end
1143
+
1144
+ it 'can use quoted keys in interpolation' do
1145
+ expect(lookup('mod_a::quoted_interpolation')).to eql('-- value mod_a::a.quoted.key (from mod_a) --') # root key found in environment, no hash merge is performed
1146
+ end
1147
+
1148
+ it 'merges hashes from environment and module in interpolated expressions if hash merge is specified in lookup options' do
1149
+ expect(lookup(['mod_a::b_a', 'mod_a::b_b'])).to eql(
1150
+ ['-- value mod_a::hash_b.a (from environment) --', '-- value mod_a::hash_b.b (from mod_a) --'])
1151
+ end
1152
+ end
1153
+
1154
+ context 'using a lookup_key that uses a path' do
1155
+ let(:mod_a_files) do
1156
+ {
1157
+ 'mod_a' => {
1158
+ 'functions' => {
1159
+ 'pp_lookup_key.pp' => <<-PUPPET.unindent
1160
+ function mod_a::pp_lookup_key($key, $options, $context) {
1161
+ if !$context.cache_has_key(undef) {
1162
+ $context.cache_all(yaml_data($options, $context))
1163
+ $context.cache(undef, true)
1164
+ }
1165
+ if $context.cache_has_key($key) { $context.cached_value($key) } else { $context.not_found }
1166
+ }
1167
+ PUPPET
686
1168
  },
687
- {
688
- :key => 'abc::e',
689
- :value => { 'k1' => 'module_e1', 'k2' => 'module_e2' },
690
- :event => :found,
691
- :type => :data_provider,
692
- :name => 'FunctionModuleDataProvider',
693
- :module => 'abc'
1169
+ 'hiera.yaml' => <<-YAML.unindent,
1170
+ ---
1171
+ version: 5
1172
+ hierarchy:
1173
+ - name: "Common"
1174
+ lookup_key: mod_a::pp_lookup_key
1175
+ path: common.yaml
1176
+ YAML
1177
+ 'data' => {
1178
+ 'common.yaml' => <<-YAML.unindent
1179
+ mod_a::b: value mod_a::b (from mod_a)
1180
+ YAML
694
1181
  }
695
- ],
696
- :value => { 'k1' => 'env_e1', 'k2' => 'module_e2', 'k3' => 'env_e3' },
697
- :event => :result,
698
- :merge => :deep,
699
- :options => { 'merge_hash_arrays' => true },
700
- :type => :merge
701
1182
  }
702
- )
1183
+ }
1184
+ end
1185
+
1186
+ it 'finds data in the module' do
1187
+ expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
1188
+ end
703
1189
  end
704
- end
705
1190
 
706
- it 'will explain that "lookup_options" is an invalid key' do
707
- assemble_and_compile('${r}', "'abc::a'") do |scope|
708
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
709
- begin
710
- Puppet::Pops::Lookup.lookup('lookup_options', nil, nil, false, nil, lookup_invocation)
711
- rescue Puppet::Error
1191
+ context 'using a lookup_key that is a puppet function' do
1192
+ let(:mod_a_files) do
1193
+ {
1194
+ 'mod_a' => {
1195
+ 'functions' => {
1196
+ 'pp_lookup_key.pp' => <<-PUPPET.unindent
1197
+ function mod_a::pp_lookup_key($key, $options, $context) {
1198
+ case $key {
1199
+ 'mod_a::really_interpolated': { $context.interpolate("-- %{lookup('mod_a::a')} --") }
1200
+ 'mod_a::recursive': { lookup($key) }
1201
+ default: {
1202
+ if $context.cache_has_key(mod_a::a) {
1203
+ $context.explain || { 'reusing cache' }
1204
+ } else {
1205
+ $context.explain || { 'initializing cache' }
1206
+ $context.cache_all({
1207
+ mod_a::a => 'value mod_a::a (from mod_a)',
1208
+ mod_a::b => 'value mod_a::b (from mod_a)',
1209
+ mod_a::c => 'value mod_a::c (from mod_a)',
1210
+ mod_a::hash_a => {
1211
+ a => 'value mod_a::hash_a.a (from mod_a)',
1212
+ b => 'value mod_a::hash_a.b (from mod_a)'
1213
+ },
1214
+ mod_a::hash_b => {
1215
+ a => 'value mod_a::hash_b.a (from mod_a)',
1216
+ b => 'value mod_a::hash_b.b (from mod_a)'
1217
+ },
1218
+ mod_a::interpolated => "-- %{lookup('mod_a::a')} --",
1219
+ mod_a::a_a => "-- %{lookup('mod_a::hash_a.a')} --",
1220
+ mod_a::a_b => "-- %{lookup('mod_a::hash_a.b')} --",
1221
+ mod_a::b_a => "-- %{lookup('mod_a::hash_b.a')} --",
1222
+ mod_a::b_b => "-- %{lookup('mod_a::hash_b.b')} --",
1223
+ 'mod_a::a.quoted.key' => 'value mod_a::a.quoted.key (from mod_a)',
1224
+ mod_a::sensitive => Sensitive('reduct me please'),
1225
+ mod_a::type => Object[{name => 'FindMe', 'attributes' => {'x' => String}}],
1226
+ mod_a::version => SemVer('3.4.1'),
1227
+ mod_a::version_range => SemVerRange('>=3.4.1'),
1228
+ mod_a::timestamp => Timestamp("1994-03-25T19:30:00"),
1229
+ mod_a::timespan => Timespan("3-10:00:00")
1230
+ })
1231
+ }
1232
+ if !$context.cache_has_key($key) {
1233
+ $context.not_found
1234
+ }
1235
+ $context.explain || { "returning value for $key" }
1236
+ $context.cached_value($key)
1237
+ }
1238
+ }
1239
+ }
1240
+ PUPPET
1241
+ },
1242
+ 'hiera.yaml' => <<-YAML.unindent,
1243
+ ---
1244
+ version: 5
1245
+ hierarchy:
1246
+ - name: "Common"
1247
+ lookup_key: mod_a::pp_lookup_key
1248
+ YAML
1249
+ }
1250
+ }
1251
+ end
1252
+
1253
+ it 'finds data in the module' do
1254
+ expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
1255
+ end
1256
+
1257
+ it 'environment data has higher priority than module data' do
1258
+ expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')
1259
+ end
1260
+
1261
+ it 'finds quoted keys in the module' do
1262
+ expect(lookup('"mod_a::a.quoted.key"')).to eql('value mod_a::a.quoted.key (from mod_a)')
1263
+ end
1264
+
1265
+ it 'will not resolve interpolated expressions' do
1266
+ expect(lookup('mod_a::interpolated')).to eql("-- %{lookup('mod_a::a')} --")
1267
+ end
1268
+
1269
+ it 'resolves interpolated expressions using Context#interpolate' do
1270
+ expect(lookup('mod_a::really_interpolated')).to eql("-- value mod_a::a (from environment) --")
1271
+ end
1272
+
1273
+ it 'will not merge hashes from environment and module unless strategy hash is used' do
1274
+ expect(lookup('mod_a::hash_a')).to eql({ 'a' => 'value mod_a::hash_a.a (from environment)' })
1275
+ end
1276
+
1277
+ it 'merges hashes from environment and module when merge strategy hash is used' do
1278
+ expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql({ 'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)' })
1279
+ end
1280
+
1281
+ it 'traps recursive lookup trapped' do
1282
+ expect(explain('mod_a::recursive')).to include('Recursive lookup detected')
1283
+ end
1284
+
1285
+ it 'private cache is persisted over multiple calls' do
1286
+ collect_notices("notice(lookup('mod_a::b')) notice(lookup('mod_a::c'))", true)
1287
+ expect(notices).to eql(['value mod_a::b (from mod_a)', 'value mod_a::c (from mod_a)'])
1288
+ expect(explanation).to match(/initializing cache.*reusing cache/m)
1289
+ expect(explanation).not_to match(/initializing cache.*initializing cache/m)
1290
+ end
1291
+
1292
+ it 'the same key is requested only once' do
1293
+ collect_notices("notice(lookup('mod_a::b')) notice(lookup('mod_a::b'))", true)
1294
+ expect(notices).to eql(['value mod_a::b (from mod_a)', 'value mod_a::b (from mod_a)'])
1295
+ expect(explanation).to match(/Found key: "mod_a::b".*Found key: "mod_a::b"/m)
1296
+ expect(explanation).to match(/returning value for mod_a::b/m)
1297
+ expect(explanation).not_to match(/returning value for mod_a::b.*returning value for mod_a::b/m)
1298
+ end
1299
+
1300
+ context 'and calling function via API' do
1301
+ it 'finds and delivers rich data' do
1302
+ collect_notices("notice('success')") do |scope|
1303
+ expect(lookup_func.call(scope, 'mod_a::sensitive')).to be_a(Puppet::Pops::Types::PSensitiveType::Sensitive)
1304
+ expect(lookup_func.call(scope, 'mod_a::type')).to be_a(Puppet::Pops::Types::PObjectType)
1305
+ expect(lookup_func.call(scope, 'mod_a::version')).to eql(SemanticPuppet::Version.parse('3.4.1'))
1306
+ expect(lookup_func.call(scope, 'mod_a::version_range')).to eql(SemanticPuppet::VersionRange.parse('>=3.4.1'))
1307
+ expect(lookup_func.call(scope, 'mod_a::timestamp')).to eql(Puppet::Pops::Time::Timestamp.parse('1994-03-25T19:30:00'))
1308
+ expect(lookup_func.call(scope, 'mod_a::timespan')).to eql(Puppet::Pops::Time::Timespan.parse('3-10:00:00'))
1309
+ end
1310
+ expect(notices).to eql(['success'])
1311
+ end
712
1312
  end
713
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
714
- Invalid key "lookup_options"
715
- EOS
716
1313
  end
717
- end
718
1314
 
719
- it 'will explain that "lookup_options" is an invalid key for any key starting with "lookup_options."' do
720
- assemble_and_compile('${r}', "'abc::a'") do |scope|
721
- lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {}, true)
722
- begin
723
- Puppet::Pops::Lookup.lookup('lookup_options.subkey', nil, nil, false, nil, lookup_invocation)
724
- rescue Puppet::Error
1315
+ context 'using a data_dig that is a ruby function' do
1316
+ let(:mod_a_files) do
1317
+ {
1318
+ 'mod_a' => {
1319
+ 'lib' => {
1320
+ 'puppet' => {
1321
+ 'functions' => {
1322
+ 'mod_a' => {
1323
+ 'ruby_dig.rb' => <<-RUBY.unindent
1324
+ Puppet::Functions.create_function(:'mod_a::ruby_dig') do
1325
+ dispatch :ruby_dig do
1326
+ param 'Array[String[1]]', :segments
1327
+ param 'Hash[String,Any]', :options
1328
+ param 'Puppet::LookupContext', :context
1329
+ end
1330
+
1331
+ def ruby_dig(segments, options, context)
1332
+ sub_segments = segments.dup
1333
+ root_key = sub_segments.shift
1334
+ case root_key
1335
+ when 'mod_a::options'
1336
+ hash = { 'mod_a::options' => options }
1337
+ when 'mod_a::lookup'
1338
+ return call_function('lookup', segments.join('.'))
1339
+ else
1340
+ hash = {
1341
+ 'mod_a::a' => 'value mod_a::a (from mod_a)',
1342
+ 'mod_a::b' => 'value mod_a::b (from mod_a)',
1343
+ 'mod_a::hash_a' => {
1344
+ 'a' => 'value mod_a::hash_a.a (from mod_a)',
1345
+ 'b' => 'value mod_a::hash_a.b (from mod_a)'
1346
+ },
1347
+ 'mod_a::hash_b' => {
1348
+ 'a' => 'value mod_a::hash_b.a (from mod_a)',
1349
+ 'b' => 'value mod_a::hash_b.b (from mod_a)'
1350
+ },
1351
+ 'mod_a::interpolated' => "-- %{lookup('mod_a::a')} --",
1352
+ 'mod_a::really_interpolated' => "-- %{lookup('mod_a::a')} --",
1353
+ 'mod_a::a_a' => "-- %{lookup('mod_a::hash_a.a')} --",
1354
+ 'mod_a::a_b' => "-- %{lookup('mod_a::hash_a.b')} --",
1355
+ 'mod_a::b_a' => "-- %{lookup('mod_a::hash_b.a')} --",
1356
+ 'mod_a::b_b' => "-- %{lookup('mod_a::hash_b.b')} --",
1357
+ 'mod_a::bad_type' => :oops,
1358
+ 'mod_a::bad_type_in_hash' => { 'a' => :oops },
1359
+ }
1360
+ end
1361
+ context.not_found unless hash.include?(root_key)
1362
+ value = sub_segments.reduce(hash[root_key]) do |memo, segment|
1363
+ context.not_found unless memo.is_a?(Hash) && memo.include?(segment)
1364
+ memo[segment]
1365
+ end
1366
+ root_key == 'mod_a::really_interpolated' ? context.interpolate(value) : value
1367
+ end
1368
+ end
1369
+ RUBY
1370
+ }
1371
+ }
1372
+ }
1373
+ },
1374
+ 'hiera.yaml' => <<-YAML.unindent,
1375
+ ---
1376
+ version: 5
1377
+ hierarchy:
1378
+ - name: "Common"
1379
+ data_dig: mod_a::ruby_dig
1380
+ uri: "http://www.example.com/passed/as/option"
1381
+ options:
1382
+ option_a: Option value a
1383
+ option_b:
1384
+ x: Option value b.x
1385
+ y: Option value b.y
1386
+ YAML
1387
+ }
1388
+ }
1389
+ end
1390
+
1391
+ it 'finds data in the module' do
1392
+ expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
1393
+ end
1394
+
1395
+ it 'environment data has higher priority than module data' do
1396
+ expect(lookup('mod_a::a')).to eql('value mod_a::a (from environment)')
1397
+ end
1398
+
1399
+ it 'will not resolve interpolated expressions' do
1400
+ expect(lookup('mod_a::interpolated')).to eql("-- %{lookup('mod_a::a')} --")
1401
+ end
1402
+
1403
+ it 'resolves interpolated expressions using Context#interpolate' do
1404
+ expect(lookup('mod_a::really_interpolated')).to eql("-- value mod_a::a (from environment) --")
1405
+ end
1406
+
1407
+ it 'does not accept return of runtime type from function' do
1408
+ expect(explain('mod_a::bad_type')).to include('Value returned from Hierarchy entry "Common" has wrong type')
1409
+ end
1410
+
1411
+ it 'does not accept return of runtime type embedded in hash from function' do
1412
+ expect(explain('mod_a::bad_type_in_hash')).to include('Value returned from Hierarchy entry "Common" has wrong type')
1413
+ end
1414
+
1415
+ it 'will not merge hashes from environment and module unless strategy hash is used' do
1416
+ expect(lookup('mod_a::hash_a')).to eql({'a' => 'value mod_a::hash_a.a (from environment)'})
1417
+ end
1418
+
1419
+ it 'hierarchy entry options are passed to the function' do
1420
+ expect(lookup('mod_a::options.option_b.x')).to eql('Option value b.x')
1421
+ end
1422
+
1423
+ it 'hierarchy entry "uri" is passed as location option to the function' do
1424
+ expect(lookup('mod_a::options.uri')).to eql('http://www.example.com/passed/as/option')
1425
+ end
1426
+
1427
+ it 'recursive lookup is trapped' do
1428
+ expect(explain('mod_a::lookup.mod_a::lookup')).to include('Recursive lookup detected')
1429
+ end
1430
+
1431
+ context 'with merge strategy hash' do
1432
+ it 'merges hashes from environment and module' do
1433
+ expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql({'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)'})
1434
+ end
1435
+
1436
+ it 'will "undig" value from data_dig function, merge root hashes, and then dig to get values by subkey' do
1437
+ expect(lookup(['mod_a::hash_a.a', 'mod_a::hash_a.b'], :merge => 'hash')).to eql(
1438
+ ['value mod_a::hash_a.a (from environment)', 'value mod_a::hash_a.b (from mod_a)'])
1439
+ end
725
1440
  end
726
- expect(lookup_invocation.explainer.to_s).to eq(<<EOS)
727
- Invalid key "lookup_options"
728
- EOS
729
1441
  end
730
1442
  end
731
-
732
1443
  end
733
1444
  end