puppet 4.4.2 → 4.5.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 (218) hide show
  1. data/CONTRIBUTING.md +5 -5
  2. data/Gemfile +2 -2
  3. data/LICENSE +2 -2
  4. data/README.md +5 -0
  5. data/ext/project_data.yaml +2 -0
  6. data/lib/hiera_puppet.rb +6 -14
  7. data/lib/puppet/application/agent.rb +2 -3
  8. data/lib/puppet/data_providers/hiera_config.rb +2 -4
  9. data/lib/puppet/data_providers/hiera_interpolate.rb +12 -154
  10. data/lib/puppet/data_providers/json_data_provider_factory.rb +0 -7
  11. data/lib/puppet/data_providers/yaml_data_provider_factory.rb +2 -8
  12. data/lib/puppet/defaults.rb +70 -7
  13. data/lib/puppet/functions.rb +69 -0
  14. data/lib/puppet/functions/dig.rb +39 -0
  15. data/lib/puppet/functions/lest.rb +53 -0
  16. data/lib/puppet/functions/lookup.rb +40 -27
  17. data/lib/puppet/functions/new.rb +502 -0
  18. data/lib/puppet/functions/regsubst.rb +11 -10
  19. data/lib/puppet/functions/then.rb +74 -0
  20. data/lib/puppet/functions/type.rb +4 -4
  21. data/lib/puppet/functions/with.rb +1 -1
  22. data/lib/puppet/indirector/catalog/compiler.rb +2 -0
  23. data/lib/puppet/indirector/resource_type/parser.rb +5 -0
  24. data/lib/puppet/indirector/rest.rb +5 -1
  25. data/lib/puppet/loaders.rb +2 -0
  26. data/lib/puppet/metatype/manager.rb +19 -2
  27. data/lib/puppet/module_tool/applications/application.rb +1 -1
  28. data/lib/puppet/module_tool/skeleton/templates/generator/Gemfile +6 -2
  29. data/lib/puppet/module_tool/skeleton/templates/generator/Rakefile +19 -4
  30. data/lib/puppet/module_tool/skeleton/templates/generator/{tests → examples}/init.pp.erb +1 -1
  31. data/lib/puppet/module_tool/skeleton/templates/generator/spec/classes/init_spec.rb.erb +0 -1
  32. data/lib/puppet/network/http/api/master/v3/environment.rb +6 -2
  33. data/lib/puppet/parser/ast/pops_bridge.rb +20 -3
  34. data/lib/puppet/parser/compiler/catalog_validator/relationship_validator.rb +24 -2
  35. data/lib/puppet/parser/e4_parser_adapter.rb +13 -12
  36. data/lib/puppet/parser/environment_compiler.rb +2 -2
  37. data/lib/puppet/parser/resource.rb +14 -5
  38. data/lib/puppet/parser/scope.rb +18 -15
  39. data/lib/puppet/plugins/data_providers/data_provider.rb +19 -8
  40. data/lib/puppet/pops.rb +6 -0
  41. data/lib/puppet/pops/adapters.rb +5 -1
  42. data/lib/puppet/pops/evaluator/access_operator.rb +52 -14
  43. data/lib/puppet/pops/evaluator/compare_operator.rb +34 -4
  44. data/lib/puppet/pops/evaluator/evaluator_impl.rb +75 -22
  45. data/lib/puppet/pops/evaluator/literal_evaluator.rb +7 -6
  46. data/lib/puppet/pops/evaluator/runtime3_converter.rb +13 -1
  47. data/lib/puppet/pops/evaluator/runtime3_support.rb +14 -4
  48. data/lib/puppet/pops/functions/dispatcher.rb +1 -1
  49. data/lib/puppet/pops/issues.rb +18 -2
  50. data/lib/puppet/pops/loader/base_loader.rb +48 -7
  51. data/lib/puppet/pops/loader/dependency_loader.rb +27 -2
  52. data/lib/puppet/pops/loader/loader.rb +12 -0
  53. data/lib/puppet/pops/loader/predefined_loader.rb +29 -0
  54. data/lib/puppet/pops/loader/runtime3_type_loader.rb +57 -0
  55. data/lib/puppet/pops/loader/static_loader.rb +92 -5
  56. data/lib/puppet/pops/loader/type_definition_instantiator.rb +25 -3
  57. data/lib/puppet/pops/loaders.rb +84 -14
  58. data/lib/puppet/pops/lookup/explainer.rb +38 -1
  59. data/lib/puppet/pops/lookup/interpolation.rb +115 -0
  60. data/lib/puppet/pops/lookup/sub_lookup.rb +86 -0
  61. data/lib/puppet/pops/model/ast_transformer.rb +8 -1
  62. data/lib/puppet/pops/model/factory.rb +31 -8
  63. data/lib/puppet/pops/model/model.rb +8 -0
  64. data/lib/puppet/pops/model/model_label_provider.rb +1 -0
  65. data/lib/puppet/pops/model/model_meta.rb +7 -1
  66. data/lib/puppet/pops/model/model_tree_dumper.rb +4 -0
  67. data/lib/puppet/pops/parser/egrammar.ra +24 -7
  68. data/lib/puppet/pops/parser/eparser.rb +863 -798
  69. data/lib/puppet/pops/parser/evaluating_parser.rb +4 -0
  70. data/lib/puppet/pops/parser/locator.rb +8 -4
  71. data/lib/puppet/pops/pcore.rb +30 -0
  72. data/lib/puppet/pops/types/class_loader.rb +2 -4
  73. data/lib/puppet/pops/types/implementation_registry.rb +146 -0
  74. data/lib/puppet/pops/types/iterable.rb +4 -4
  75. data/lib/puppet/pops/types/p_object_type.rb +846 -0
  76. data/lib/puppet/pops/types/p_runtime_type.rb +102 -0
  77. data/lib/puppet/pops/types/p_sem_ver_range_type.rb +164 -0
  78. data/lib/puppet/pops/types/p_sem_ver_type.rb +113 -0
  79. data/lib/puppet/pops/types/puppet_object.rb +21 -0
  80. data/lib/puppet/pops/types/ruby_generator.rb +258 -0
  81. data/lib/puppet/pops/types/string_converter.rb +922 -0
  82. data/lib/puppet/pops/types/type_calculator.rb +29 -5
  83. data/lib/puppet/pops/types/type_conversion_error.rb +15 -0
  84. data/lib/puppet/pops/types/type_factory.rb +49 -16
  85. data/lib/puppet/pops/types/type_formatter.rb +335 -112
  86. data/lib/puppet/pops/types/type_mismatch_describer.rb +110 -29
  87. data/lib/puppet/pops/types/type_parser.rb +205 -197
  88. data/lib/puppet/pops/types/types.rb +481 -103
  89. data/lib/puppet/pops/validation.rb +1 -1
  90. data/lib/puppet/pops/validation/checker4_0.rb +66 -4
  91. data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
  92. data/lib/puppet/pops/visitor.rb +3 -1
  93. data/lib/puppet/property.rb +1 -1
  94. data/lib/puppet/provider/augeas/augeas.rb +1 -1
  95. data/lib/puppet/provider/package/pip.rb +64 -20
  96. data/lib/puppet/provider/package/rpm.rb +112 -0
  97. data/lib/puppet/provider/package/yum.rb +7 -68
  98. data/lib/puppet/provider/service/daemontools.rb +3 -3
  99. data/lib/puppet/provider/service/init.rb +4 -2
  100. data/lib/puppet/provider/service/runit.rb +3 -3
  101. data/lib/puppet/provider/service/smf.rb +6 -3
  102. data/lib/puppet/provider/service/systemd.rb +59 -73
  103. data/lib/puppet/reference/providers.rb +1 -2
  104. data/lib/puppet/resource.rb +54 -37
  105. data/lib/puppet/resource/catalog.rb +31 -29
  106. data/lib/puppet/resource/type_collection.rb +23 -8
  107. data/lib/puppet/settings.rb +4 -2
  108. data/lib/puppet/settings/base_setting.rb +9 -3
  109. data/lib/puppet/settings/symbolic_enum_setting.rb +17 -0
  110. data/lib/puppet/test/test_helper.rb +0 -1
  111. data/lib/puppet/type.rb +9 -3
  112. data/lib/puppet/type/exec.rb +17 -17
  113. data/lib/puppet/type/file.rb +12 -0
  114. data/lib/puppet/type/file/content.rb +6 -6
  115. data/lib/puppet/type/file/ensure.rb +4 -4
  116. data/lib/puppet/type/file/source.rb +4 -4
  117. data/lib/puppet/type/file/target.rb +2 -2
  118. data/lib/puppet/type/mount.rb +18 -1
  119. data/lib/puppet/type/package.rb +3 -3
  120. data/lib/puppet/type/schedule.rb +4 -4
  121. data/lib/puppet/type/service.rb +15 -0
  122. data/lib/puppet/type/sshkey.rb +5 -3
  123. data/lib/puppet/type/tidy.rb +3 -3
  124. data/lib/puppet/type/zone.rb +5 -5
  125. data/lib/puppet/util/feature.rb +1 -1
  126. data/lib/puppet/util/monkey_patches.rb +8 -0
  127. data/lib/puppet/util/network_device/cisco/device.rb +16 -6
  128. data/lib/puppet/util/network_device/cisco/interface.rb +5 -6
  129. data/lib/puppet/util/plist.rb +3 -3
  130. data/lib/puppet/version.rb +1 -1
  131. data/spec/fixtures/unit/application/environments/production/data/common.yaml +13 -0
  132. data/spec/fixtures/unit/data_providers/environments/production/modules/abc/lib/puppet/functions/abc/data.rb +2 -1
  133. data/spec/fixtures/unit/data_providers/environments/production/modules/abc/manifests/init.pp +2 -1
  134. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/data/empty_key.json +1 -0
  135. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/hiera.yaml +5 -0
  136. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/manifests/init.pp +2 -0
  137. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/metadata.json +9 -0
  138. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/data/empty_key.yaml +1 -0
  139. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/hiera.yaml +5 -0
  140. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/manifests/init.pp +2 -0
  141. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/metadata.json +9 -0
  142. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/data/empty.yaml +1 -0
  143. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/lib/puppet/type/usee_type.rb +5 -0
  144. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/manifests/init.pp +6 -0
  145. data/spec/fixtures/unit/provider/service/smf/svcs.out +4 -3
  146. data/spec/integration/module_tool/tar/mini_spec.rb +27 -27
  147. data/spec/integration/parser/catalog_spec.rb +14 -2
  148. data/spec/integration/parser/compiler_spec.rb +94 -3
  149. data/spec/integration/parser/resource_expressions_spec.rb +1 -1
  150. data/spec/integration/resource/type_collection_spec.rb +8 -0
  151. data/spec/lib/puppet_spec/compiler.rb +11 -4
  152. data/spec/shared_contexts/types_setup.rb +4 -0
  153. data/spec/unit/application/lookup_spec.rb +91 -9
  154. data/spec/unit/appmgmt_spec.rb +44 -35
  155. data/spec/unit/capability_spec.rb +33 -53
  156. data/spec/unit/data_providers/function_data_provider_spec.rb +19 -1
  157. data/spec/unit/data_providers/hiera_data_provider_spec.rb +1 -1
  158. data/spec/unit/defaults_spec.rb +18 -0
  159. data/spec/unit/functions/assert_type_spec.rb +1 -1
  160. data/spec/unit/functions/dig_spec.rb +58 -0
  161. data/spec/unit/functions/lest_spec.rb +34 -0
  162. data/spec/unit/functions/lookup_spec.rb +108 -2
  163. data/spec/unit/functions/new_spec.rb +543 -0
  164. data/spec/unit/functions/regsubst_spec.rb +8 -0
  165. data/spec/unit/functions/then_spec.rb +40 -0
  166. data/spec/unit/functions4_spec.rb +78 -10
  167. data/spec/unit/hiera_puppet_spec.rb +49 -8
  168. data/spec/unit/indirector/resource_type/parser_spec.rb +5 -0
  169. data/spec/unit/indirector/rest_spec.rb +12 -0
  170. data/spec/unit/network/http/api/master/v3/environment_spec.rb +60 -0
  171. data/spec/unit/node/environment_spec.rb +10 -0
  172. data/spec/unit/parser/compiler_spec.rb +20 -1
  173. data/spec/unit/parser/functions/create_resources_spec.rb +2 -2
  174. data/spec/unit/parser/functions/shared.rb +1 -1
  175. data/spec/unit/parser/resource_spec.rb +8 -1
  176. data/spec/unit/parser/scope_spec.rb +45 -0
  177. data/spec/unit/pops/evaluator/access_ops_spec.rb +14 -0
  178. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +13 -5
  179. data/spec/unit/pops/loaders/static_loader_spec.rb +92 -1
  180. data/spec/unit/{data_providers/hiera_interpolation_spec.rb → pops/lookup/interpolation_spec.rb} +7 -5
  181. data/spec/unit/pops/parser/lexer2_spec.rb +2 -9
  182. data/spec/unit/pops/parser/parse_application_spec.rb +3 -8
  183. data/spec/unit/pops/parser/parse_basic_expressions_spec.rb +19 -0
  184. data/spec/unit/pops/parser/parse_capabilities_spec.rb +3 -10
  185. data/spec/unit/pops/parser/parse_site_spec.rb +19 -10
  186. data/spec/unit/pops/parser/parser_rspec_helper.rb +0 -4
  187. data/spec/unit/pops/types/enumeration_spec.rb +13 -12
  188. data/spec/unit/pops/types/iterable_spec.rb +2 -2
  189. data/spec/unit/pops/types/p_object_type_spec.rb +1060 -0
  190. data/spec/unit/pops/types/p_sem_ver_type_spec.rb +285 -0
  191. data/spec/unit/pops/types/recursion_guard_spec.rb +19 -17
  192. data/spec/unit/pops/types/ruby_generator_spec.rb +261 -0
  193. data/spec/unit/pops/types/string_converter_spec.rb +904 -0
  194. data/spec/unit/pops/types/type_calculator_spec.rb +430 -406
  195. data/spec/unit/pops/types/type_factory_spec.rb +119 -104
  196. data/spec/unit/pops/types/type_formatter_spec.rb +73 -6
  197. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +2 -2
  198. data/spec/unit/pops/types/type_parser_spec.rb +54 -15
  199. data/spec/unit/pops/types/types_spec.rb +113 -8
  200. data/spec/unit/pops/validator/validator_spec.rb +84 -10
  201. data/spec/unit/provider/package/pip3_spec.rb +9 -270
  202. data/spec/unit/provider/package/pip_spec.rb +85 -30
  203. data/spec/unit/provider/package/rpm_spec.rb +160 -3
  204. data/spec/unit/provider/package/yum_spec.rb +23 -134
  205. data/spec/unit/provider/service/smf_spec.rb +14 -2
  206. data/spec/unit/provider/service/systemd_spec.rb +33 -41
  207. data/spec/unit/resource/capability_finder_spec.rb +10 -2
  208. data/spec/unit/settings/file_setting_spec.rb +6 -0
  209. data/spec/unit/transaction/additional_resource_generator_spec.rb +80 -65
  210. data/spec/unit/type/mount_spec.rb +51 -10
  211. data/spec/unit/type/service_spec.rb +16 -0
  212. data/spec/unit/type_spec.rb +14 -0
  213. data/spec/unit/util/feature_spec.rb +1 -1
  214. data/spec/unit/util/monkey_patches_spec.rb +60 -0
  215. data/spec/unit/util/network_device/cisco/device_spec.rb +1 -1
  216. metadata +63 -11
  217. data/lib/puppet/pops/types/types_meta.rb +0 -0
  218. data/spec/integration/provider/package_spec.rb +0 -35
@@ -42,14 +42,36 @@ class TypeDefinitionInstantiator
42
42
  # the loader is known - hence this mechanism
43
43
  private_loader = loader.private_loader
44
44
  Adapters::LoaderAdapter.adapt(type_definition).loader = private_loader
45
-
46
- Types::PTypeAliasType.new(name, type_definition.type_expr)
45
+ create_type(type_definition, loader)
47
46
  end
48
47
 
49
48
  def self.create_from_model(type_definition, loader)
49
+ typed_name = Loader::TypedName.new(:type, type_definition.name.downcase)
50
+ type = create_type(type_definition, loader)
51
+ loader.set_entry(
52
+ typed_name,
53
+ type,
54
+ Adapters::SourcePosAdapter.adapt(type_definition).to_uri)
55
+ type
56
+ end
57
+
58
+ def self.create_type(type_definition, loader)
59
+ type_expr = type_definition.type_expr
50
60
  name = type_definition.name
51
- [Loader::TypedName.new(:type, name.downcase), Types::PTypeAliasType.new(name, type_definition.type_expr)]
61
+ if object_definition?(type_expr)
62
+ # No need for an alias. The Object type itself will receive the name instead
63
+ i12n_hash_expr = type_expr.keys.empty? ? nil : type_expr.keys[0]
64
+ Types::PObjectType.new(name, i12n_hash_expr)
65
+ else
66
+ Types::PTypeAliasType.new(name, type_expr)
67
+ end
68
+ end
69
+ private_class_method :create_type
70
+
71
+ def self.object_definition?(te)
72
+ te.is_a?(Model::AccessExpression) && (left = te.left_expr).is_a?(Model::QualifiedReference) && left.cased_value == 'Object'
52
73
  end
74
+ private_class_method :object_definition?
53
75
  end
54
76
  end
55
77
  end
@@ -6,10 +6,9 @@ class Loaders
6
6
  attr_reader :puppet_system_loader
7
7
  attr_reader :public_environment_loader
8
8
  attr_reader :private_environment_loader
9
+ attr_reader :implementation_registry
9
10
 
10
11
  def initialize(environment)
11
- # The static loader can only be changed after a reboot
12
- @@static_loader ||= Loader::StaticLoader.new()
13
12
 
14
13
  # Create the set of loaders
15
14
  # 1. Puppet, loads from the "running" puppet - i.e. bundled functions, types, extension points and extensions
@@ -26,28 +25,81 @@ class Loaders
26
25
  #
27
26
  @private_environment_loader = create_environment_loader(environment)
28
27
 
29
- # 3. module loaders are set up from the create_environment_loader, they register themselves
28
+ # 3. The implementation registry maintains mappings between Puppet types and Runtime types for
29
+ # the current environment
30
+ @implementation_registry = Types::ImplementationRegistry.new(@private_environment_loader)
31
+ Pcore.init(@puppet_system_loader, @implementation_registry)
32
+
33
+ # 4. module loaders are set up from the create_environment_loader, they register themselves
30
34
  end
31
35
 
32
36
  # Clears the cached static and puppet_system loaders (to enable testing)
33
37
  #
34
38
  def self.clear
35
39
  @@static_loader = nil
36
- @puppet_system_loader = nil
37
40
  end
38
41
 
39
- # Finds the `Loaders` instance by looking up the :loaders in the global Puppet context and then uses it to
40
- # find the appropriate loader for the given `module_name`, or for the environment in case `module_name`
41
- # is `nil` or empty.
42
+ # Calls {#loaders} to obtain the {{Loaders}} instance and then uses it to find the appropriate loader
43
+ # for the given `module_name`, or for the environment in case `module_name` is `nil` or empty.
42
44
  #
43
45
  # @param module_name [String,nil] the name of the module
44
46
  # @return [Loader::Loader] the found loader
45
47
  # @raise [Puppet::ParseError] if no loader can be found
46
48
  # @api private
47
49
  def self.find_loader(module_name)
50
+ loaders.find_loader(module_name)
51
+ end
52
+
53
+ def self.static_loader
54
+ # The static loader can only be changed after a reboot
55
+ @@static_loader ||= Loader::StaticLoader.new()
56
+ end
57
+
58
+ def self.implementation_registry
59
+ loaders = Puppet.lookup(:loaders) { nil }
60
+ loaders.nil? ? nil : loaders.implementation_registry
61
+ end
62
+
63
+ def register_implementations(*obj_classes)
64
+ loader = @private_environment_loader
65
+ types = obj_classes.map do |obj_class|
66
+ type = obj_class._ptype
67
+ typed_name = Loader::Loader::TypedName.new(:type, type.name.downcase)
68
+ entry = loader.loaded_entry(typed_name)
69
+ loader.set_entry(typed_name, type, obj_class._plocation) if entry.nil? || entry.value.nil?
70
+ type
71
+ end
72
+ # Resolve lazy so that all types can cross reference eachother
73
+ parser = Types::TypeParser.new
74
+ types.each { |type| type.resolve(parser, loader) }
75
+ end
76
+
77
+ # Register the given type with the Runtime3TypeLoader. The registration will not happen unless
78
+ # the type system has been initialized.
79
+ #
80
+ # @param name [String,Symbol] the name of the entity being set
81
+ # @param origin [URI] the origin or the source where the type is defined
82
+ # @api private
83
+ def self.register_runtime3_type(name, origin)
84
+ loaders = Puppet.lookup(:loaders) { nil }
85
+ unless loaders.nil?
86
+ name = name.to_s
87
+ caps_name = Types::TypeFormatter.singleton.capitalize_segments(name)
88
+ typed_name = Loader::Loader::TypedName.new(:type, name.downcase)
89
+ loaders.runtime3_type_loader.set_entry(typed_name, Types::PResourceType.new(caps_name), origin)
90
+ end
91
+ nil
92
+ end
93
+
94
+ # Finds the `Loaders` instance by looking up the :loaders in the global Puppet context
95
+ #
96
+ # @return [Loaders] the loaders instance
97
+ # @raise [Puppet::ParseError] if loader has been bound to the global context
98
+ # @api private
99
+ def self.loaders
48
100
  loaders = Puppet.lookup(:loaders) { nil }
49
101
  raise Puppet::ParseError, "Internal Error: Puppet Context ':loaders' missing" if loaders.nil?
50
- loaders.find_loader(module_name)
102
+ loaders
51
103
  end
52
104
 
53
105
  # Finds the appropriate loader for the given `module_name`, or for the environment in case `module_name`
@@ -77,13 +129,17 @@ class Loaders
77
129
  end
78
130
 
79
131
  def static_loader
80
- @@static_loader
132
+ self.class.static_loader
81
133
  end
82
134
 
83
135
  def puppet_system_loader
84
136
  @puppet_system_loader
85
137
  end
86
138
 
139
+ def runtime3_type_loader
140
+ @runtime3_type_loader
141
+ end
142
+
87
143
  def public_loader_for_module(module_name)
88
144
  md = @module_resolver[module_name] || (return nil)
89
145
  # Note, this loader is not resolved until there is interest in the visibility of entities from the
@@ -130,12 +186,16 @@ class Loaders
130
186
  module_name = nil
131
187
  loader_name = "environment:#{environment.name}"
132
188
  env_conf = Puppet.lookup(:environments).get_conf(environment.name)
189
+
190
+ # Create the 3.x resource type loader
191
+ @runtime3_type_loader = Loader::Runtime3TypeLoader.new(puppet_system_loader, environment)
192
+
133
193
  if env_conf.nil? || !env_conf.is_a?(Puppet::Settings::EnvironmentConf)
134
194
  # Not a real directory environment, cannot work as a module TODO: Drop when legacy env are dropped?
135
- loader = Loader::SimpleEnvironmentLoader.new(puppet_system_loader, loader_name)
195
+ loader = Loader::SimpleEnvironmentLoader.new(@runtime3_type_loader, loader_name)
136
196
  else
137
197
  # View the environment as a module to allow loading from it - this module is always called 'environment'
138
- loader = Loader::ModuleLoaders.module_loader_from(puppet_system_loader, self, 'environment', env_conf.path_to_env)
198
+ loader = Loader::ModuleLoaders.module_loader_from(@runtime3_type_loader, self, 'environment', env_conf.path_to_env)
139
199
  end
140
200
 
141
201
  # An environment has a module path even if it has a null loader
@@ -270,9 +330,19 @@ class Loaders
270
330
 
271
331
  def create_loader_with_only_dependencies_visible(from_module_data)
272
332
  if from_module_data.unmet_dependencies?
273
- Puppet.warning("ModuleLoader: module '#{from_module_data.name}' has unresolved dependencies"+
274
- " - it will only see those that are resolved."+
275
- " Use 'puppet module list --tree' to see information about modules")
333
+ if Puppet[:strict] != :off
334
+ msg = "ModuleLoader: module '#{from_module_data.name}' has unresolved dependencies" \
335
+ " - it will only see those that are resolved." \
336
+ " Use 'puppet module list --tree' to see information about modules"
337
+ case Puppet[:strict]
338
+ when :error
339
+ raise LoaderError.new(msg)
340
+ when :warning
341
+ Puppet.warn_once(:unresolved_module_dependencies,
342
+ "unresolved_dependencies_for_module_#{from_module_data.name}",
343
+ msg)
344
+ end
345
+ end
276
346
  end
277
347
  dependency_loaders = from_module_data.dependency_names.collect { |name| @index[name].public_loader }
278
348
  Loader::DependencyLoader.new(from_module_data.public_loader, from_module_data.name, dependency_loaders)
@@ -367,13 +367,46 @@ module Puppet::Pops::Lookup
367
367
  end
368
368
  end
369
369
 
370
+ class ExplainSubLookup < ExplainTreeNode
371
+ def initialize(parent, sub_key)
372
+ super(parent)
373
+ @sub_key = sub_key
374
+ end
375
+
376
+ def dump_on(io, indent, first_indent)
377
+ io << indent << 'Sub key: "' << @sub_key.join('.') << "\"\n"
378
+ indent = increase_indent(indent)
379
+ branches.each {|b| b.dump_on(io, indent, indent)}
380
+ dump_outcome(io, indent)
381
+ end
382
+
383
+ def type
384
+ :sub_key
385
+ end
386
+ end
387
+
388
+ class ExplainKeySegment < ExplainTreeNode
389
+ def initialize(parent, segment)
390
+ super(parent)
391
+ @segment = segment
392
+ end
393
+
394
+ def dump_on(io, indent, first_indent)
395
+ dump_outcome(io, indent)
396
+ end
397
+
398
+ def type
399
+ :segment
400
+ end
401
+ end
402
+
370
403
  class ExplainScope < ExplainTreeNode
371
404
  def initialize(parent)
372
405
  super(parent)
373
406
  end
374
407
 
375
408
  def dump_on(io, indent, first_indent)
376
- io << indent << 'Global Scope' << "\"\n"
409
+ io << indent << 'Global Scope' << "\n"
377
410
  indent = increase_indent(indent)
378
411
  dump_outcome(io, indent)
379
412
  end
@@ -406,6 +439,10 @@ module Puppet::Pops::Lookup
406
439
  ExplainMerge.new(@current, qualifier)
407
440
  when :scope
408
441
  ExplainScope.new(@current)
442
+ when :sub_lookup
443
+ ExplainSubLookup.new(@current, qualifier)
444
+ when :segment
445
+ ExplainKeySegment.new(@current, qualifier)
409
446
  when :meta, :data
410
447
  ExplainTop.new(@current, qualifier_type, qualifier)
411
448
  when :invalid_key
@@ -0,0 +1,115 @@
1
+ require_relative 'sub_lookup'
2
+
3
+ # Add support for Hiera-like interpolation expressions. The expressions may contain keys that uses dot-notation
4
+ # to further navigate into hashes and arrays
5
+ #
6
+ module Puppet::Pops
7
+ module Lookup
8
+ module Interpolation
9
+ include SubLookup
10
+
11
+ def interpolate(subject, lookup_invocation, allow_methods)
12
+ case subject
13
+ when String
14
+ subject.index('%{').nil? ? subject : interpolate_string(subject, lookup_invocation, allow_methods)
15
+ when Array
16
+ subject.map { |element| interpolate(element, lookup_invocation, allow_methods) }
17
+ when Hash
18
+ Hash[subject.map { |k, v| [k, interpolate(v, lookup_invocation, allow_methods)] }]
19
+ else
20
+ subject
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ EMPTY_INTERPOLATIONS = {
27
+ '' => true,
28
+ '::' => true,
29
+ '""' => true,
30
+ "''" => true,
31
+ '"::"' => true,
32
+ "'::'" => true
33
+ }.freeze
34
+
35
+ # Matches a key that is quoted using a matching pair of either single or double quotes.
36
+ QUOTED_KEY = /^(?:"([^"]+)"|'([^']+)')$/
37
+
38
+ def interpolate_string(subject, lookup_invocation, allow_methods)
39
+ lookup_invocation.with(:interpolate, subject) do
40
+ subject.gsub(/%\{([^\}]*)\}/) do |match|
41
+ expr = $1
42
+ # Leading and trailing spaces inside an interpolation expression are insignificant
43
+ expr.strip!
44
+ value = nil
45
+ unless EMPTY_INTERPOLATIONS[expr]
46
+ method_key, key = get_method_and_data(expr, allow_methods)
47
+ is_alias = method_key == 'alias'
48
+
49
+ # Alias is only permitted if the entire string is equal to the interpolate expression
50
+ raise Puppet::DataBinding::LookupError, "'alias' interpolation is only permitted if the expression is equal to the entire string" if is_alias && subject != match
51
+
52
+ segments = split_key(key) { |problem| Puppet::DataBinding::LookupError.new("#{problem} in string: #{subject}") }
53
+ root_key = segments.shift
54
+ value = interpolate_method(method_key).call(root_key, lookup_invocation)
55
+ value = sub_lookup(key, lookup_invocation, segments, value) unless segments.empty?
56
+ value = lookup_invocation.check(key) { interpolate(value, lookup_invocation, allow_methods) }
57
+
58
+ # break gsub and return value immediately if this was an alias substitution. The value might be something other than a String
59
+ return value if is_alias
60
+ end
61
+ value || ''
62
+ end
63
+ end
64
+ end
65
+
66
+ def interpolate_method(method_key)
67
+ @@interpolate_methods ||= begin
68
+ global_lookup = lambda { |key, lookup_invocation| Lookup.lookup(key, nil, '', true, nil, lookup_invocation) }
69
+ scope_lookup = lambda do |key, lookup_invocation|
70
+ lookup_invocation.with(:scope, nil) do
71
+ ovr = lookup_invocation.override_values
72
+ if ovr.include?(key)
73
+ lookup_invocation.report_found_in_overrides(key, ovr[key])
74
+ else
75
+ scope = lookup_invocation.scope
76
+ if scope.include?(key)
77
+ lookup_invocation.report_found(key, scope[key])
78
+ else
79
+ defaults = lookup_invocation.default_values
80
+ if defaults.include?(key)
81
+ lookup_invocation.report_found_in_defaults(key, defaults[key])
82
+ else
83
+ nil
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ {
91
+ 'lookup' => global_lookup,
92
+ 'hiera' => global_lookup, # this is just an alias for 'lookup'
93
+ 'alias' => global_lookup, # same as 'lookup' but expression must be entire string and result is not subject to string substitution
94
+ 'scope' => scope_lookup,
95
+ 'literal' => lambda { |key, _| key }
96
+ }.freeze
97
+ end
98
+ interpolate_method = @@interpolate_methods[method_key]
99
+ raise Puppet::DataBinding::LookupError, "Unknown interpolation method '#{method_key}'" unless interpolate_method
100
+ interpolate_method
101
+ end
102
+
103
+ def get_method_and_data(data, allow_methods)
104
+ if match = data.match(/^(\w+)\((?:["]([^"]+)["]|[']([^']+)['])\)$/)
105
+ raise Puppet::DataBinding::LookupError, 'Interpolation using method syntax is not allowed in this context' unless allow_methods
106
+ key = match[1]
107
+ data = match[2] || match[3] # double or single qouted
108
+ else
109
+ key = 'scope'
110
+ end
111
+ [key, data]
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,86 @@
1
+ module Puppet::Pops
2
+ module Lookup
3
+ module SubLookup
4
+ # Split key into segments. A segment may be a quoted string (both single and double quotes can
5
+ # be used) and the segment separator is the '.' character. Whitespace will be trimmed off on
6
+ # both sides of each segment. Whitespace within quotes are not trimmed.
7
+ #
8
+ # If the key cannot be parsed, this method will yield a string describing the problem to a one
9
+ # parameter block. The block must return an exception instance.
10
+ #
11
+ # @param key [String] the string to split
12
+ # @return Array<String> the array of segments
13
+ # @yieldparam problem [String] the problem, i.e. 'Syntax error'
14
+ # @yieldreturn [Exception] the exception to raise
15
+ #
16
+ # @api public
17
+ def split_key(key)
18
+ segments = key.split(/(\s*"[^"]+"\s*|\s*'[^']+'\s*|[^'".]+)/)
19
+ if segments.empty?
20
+ # Only happens if the original key was an empty string
21
+ ''
22
+ elsif segments.shift == ''
23
+ count = segments.size
24
+ raise yield('Syntax error') unless count > 0
25
+
26
+ segments.keep_if { |seg| seg != '.' }
27
+ raise yield('Syntax error') unless segments.size * 2 == count + 1
28
+ segments.map! do |segment|
29
+ segment.strip!
30
+ segment.start_with?('"') || segment.start_with?("'") ? segment[1..-2] : segment
31
+ end
32
+ else
33
+ raise yield('Syntax error')
34
+ end
35
+ end
36
+
37
+ # Perform a sub-lookup using the given _segments_ to access the given _value_. Each segment must be a string. A string
38
+ # consisting entirely of digits will be treated as an indexed lookup which means that the value that it is applied to
39
+ # must be an array. Other types of segments will expect that the given value is something other than a String that
40
+ # implements the '#[]' method.
41
+ #
42
+ # @param key [String] the original key (only used for error messages)
43
+ # @param lookup_invocation [Invocation] The current lookup invocation
44
+ # @param segments [Array<String>] the segments to use for lookup
45
+ # @param value [Object] the value to access using the segments
46
+ # @return [Object] the value obtained when accessing the value
47
+ #
48
+ # @api public
49
+ def sub_lookup(key, lookup_invocation, segments, value)
50
+ lookup_invocation.with(:sub_lookup, segments) do
51
+ segments.each do |segment|
52
+ lookup_invocation.with(:segment, segment) do
53
+ if value.nil?
54
+ lookup_invocation.report_not_found(segment)
55
+ throw :no_such_key
56
+ end
57
+ if segment =~ /^[0-9]+$/
58
+ segment = segment.to_i
59
+ unless value.instance_of?(Array)
60
+ raise Puppet::DataBinding::LookupError,
61
+ "Data Provider type mismatch: Got #{value.class.name} when Array was expected to access value using '#{segment}' from key '#{key}'"
62
+ end
63
+ unless segment < value.size
64
+ lookup_invocation.report_not_found(segment)
65
+ throw :no_such_key
66
+ end
67
+ else
68
+ unless value.respond_to?(:'[]') && !(value.instance_of?(Array) || value.instance_of?(String))
69
+ raise Puppet::DataBinding::LookupError,
70
+ "Data Provider type mismatch: Got #{value.class.name} when a hash-like object was expected to access value using '#{segment}' from key '#{key}'"
71
+ end
72
+ unless value.include?(segment)
73
+ lookup_invocation.report_not_found(segment)
74
+ throw :no_such_key
75
+ end
76
+ end
77
+ value = value[segment]
78
+ lookup_invocation.report_found(segment, value)
79
+ end
80
+ end
81
+ value
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end