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
@@ -1,13 +1,13 @@
1
1
  require 'puppet/parser/compiler'
2
2
 
3
3
  class Puppet::Parser::EnvironmentCompiler < Puppet::Parser::Compiler
4
- def self.compile(env)
4
+ def self.compile(env, code_id=nil)
5
5
  begin
6
6
  env.check_for_reparse
7
7
 
8
8
  node = Puppet::Node.new(env)
9
9
  node.environment = env
10
- new(node).compile
10
+ new(node, :code_id => code_id).compile
11
11
  rescue => detail
12
12
  message = "#{detail} in environment #{env.name}"
13
13
  Puppet.log_exception(detail, message)
@@ -182,11 +182,20 @@ class Puppet::Parser::Resource < Puppet::Resource
182
182
  alias []= set_parameter
183
183
 
184
184
  def to_hash
185
- @parameters.inject({}) do |hash, ary|
186
- param = ary[1]
187
- # Skip "undef" and nil values.
188
- hash[param.name] = param.value if param.value != :undef && !param.value.nil?
189
- hash
185
+ @parameters.reduce({}) do |result, (_, param)|
186
+ value = param.value
187
+ value = (value == :undef) ? nil : value
188
+
189
+ unless value.nil?
190
+ case param.name
191
+ when :before, :subscribe, :notify, :require
192
+ value = value.flatten if value.is_a?(Array)
193
+ result[param.name] = value
194
+ else
195
+ result[param.name] = value
196
+ end
197
+ end
198
+ result
190
199
  end
191
200
  end
192
201
 
@@ -504,29 +504,32 @@ class Puppet::Parser::Scope
504
504
  end
505
505
 
506
506
  UNDEFINED_VARIABLES_KIND = 'undefined_variables'.freeze
507
+ DEPRECATION_KIND = 'deprecation'.freeze
508
+
509
+ # The exception raised when a throw is uncaught is different in different versions
510
+ # of ruby. In >=2.2.0 it is UncaughtThrowError (which did not exist prior to this)
511
+ #
512
+ UNCAUGHT_THROW_EXCEPTION = defined?(UncaughtThrowError) ? UncaughtThrowError : ArgumentError
507
513
 
508
514
  def variable_not_found(name, reason=nil)
509
- # Built in variables always exist
510
- if BUILT_IN_VARS.include?(name)
515
+ # Built in variables and numeric variables always exist
516
+ if BUILT_IN_VARS.include?(name) || name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME
511
517
  return nil
512
518
  end
513
- if Puppet[:strict_variables]
519
+ begin
514
520
  throw(:undefined_variable, reason)
515
- else
516
- # Always warn, unfortunately without location (unless given in "reason") since
517
- # a location is in most cases not given to scope (operator [], and lookupvar), and
518
- # would be too expensive to always give.
519
- # The ideal solution would be to always throw :undefined_variable, but that has to
520
- # wait until a major release. It would then force all callers of scope to deal with
521
- # the case of :undefined_variable. (Should check with include? first or catch the throw).
522
- # Use deprecation warning to enable turning off these warnings, and to ensure each variable
523
- # is only logged once.
524
- unless name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME
521
+ rescue UNCAUGHT_THROW_EXCEPTION
522
+ case Puppet[:strict]
523
+ when :off
524
+ # do nothing
525
+ when :warning
525
526
  Puppet.warn_once(UNDEFINED_VARIABLES_KIND, "Variable: #{name}",
526
- "Undefined variable '#{name}'; #{reason}" )
527
+ "Undefined variable '#{name}'; #{reason}" )
528
+ when :error
529
+ raise ArgumentError, "Undefined variable '#{name}'; #{reason}"
527
530
  end
528
- nil
529
531
  end
532
+ nil
530
533
  end
531
534
 
532
535
  # Retrieves the variable value assigned to the name given as an argument. The name must be a String,
@@ -1,5 +1,9 @@
1
+ require 'puppet/pops/lookup/interpolation'
2
+
1
3
  module Puppet::Plugins::DataProviders
2
4
  module DataProvider
5
+ include Puppet::Pops::Lookup::Interpolation
6
+
3
7
  # Performs a lookup with an endless recursion check.
4
8
  #
5
9
  # @param key [String] The key to lookup
@@ -19,10 +23,13 @@ module Puppet::Plugins::DataProviders
19
23
  #
20
24
  # @api public
21
25
  def unchecked_lookup(key, lookup_invocation, merge)
26
+ segments = split_key(key)
27
+ root_key = segments.shift
22
28
  lookup_invocation.with(:data_provider, self) do
23
- hash = data(data_key(key, lookup_invocation), lookup_invocation)
24
- value = hash[key]
25
- if value || hash.include?(key)
29
+ hash = data(data_key(root_key, lookup_invocation), lookup_invocation)
30
+ value = hash[root_key]
31
+ if value || hash.include?(root_key)
32
+ value = sub_lookup(key, lookup_invocation, segments, value) unless segments.empty?
26
33
  lookup_invocation.report_found(key, post_process(value, lookup_invocation))
27
34
  else
28
35
  lookup_invocation.report_not_found(key)
@@ -31,8 +38,8 @@ module Puppet::Plugins::DataProviders
31
38
  end
32
39
  end
33
40
 
34
- # Perform optional post processing of found value. This hook is used by the hiera style
35
- # providers to perform interpolation. The default method simply returns the given _value_.
41
+ # Perform optional post processing of found value. The default implementation resolves
42
+ # interpolation expressions
36
43
  #
37
44
  # @param value [Object] The value to perform post processing on
38
45
  # @param lookup_invocation [Puppet::Pops::Lookup::Invocation] The current lookup invocation
@@ -40,7 +47,7 @@ module Puppet::Plugins::DataProviders
40
47
  #
41
48
  # @api public
42
49
  def post_process(value, lookup_invocation)
43
- value
50
+ interpolate(value, lookup_invocation, true)
44
51
  end
45
52
 
46
53
  # Gets the data from the compiler, or initializes it by calling #initialize_data if not present in the compiler.
@@ -223,6 +230,9 @@ module Puppet::Plugins::DataProviders
223
230
  #
224
231
  # @api public
225
232
  def unchecked_lookup(key, lookup_invocation, merge)
233
+ segments = split_key(key)
234
+ root_key = segments.shift
235
+
226
236
  module_name = @parent_data_provider.nil? ? nil : @parent_data_provider.data_key(key, lookup_invocation)
227
237
  lookup_invocation.with(:data_provider, self) do
228
238
  merge_strategy = Puppet::Pops::MergeStrategy.strategy(merge)
@@ -231,8 +241,9 @@ module Puppet::Plugins::DataProviders
231
241
  lookup_invocation.with(:path, path) do
232
242
  if path.exists?
233
243
  hash = load_data(path.path, module_name, lookup_invocation)
234
- value = hash[key]
235
- if value || hash.include?(key)
244
+ value = hash[root_key]
245
+ if value || hash.include?(root_key)
246
+ value = sub_lookup(key, lookup_invocation, segments, value) unless segments.empty?
236
247
  lookup_invocation.report_found(key, post_process(value, lookup_invocation))
237
248
  else
238
249
  lookup_invocation.report_not_found(key)
@@ -10,6 +10,8 @@ module Puppet
10
10
  #
11
11
  # @api public
12
12
  module Pops
13
+ require 'semantic'
14
+
13
15
  require 'puppet/pops/patterns'
14
16
  require 'puppet/pops/utils'
15
17
 
@@ -27,13 +29,16 @@ module Puppet
27
29
  require 'puppet/pops/validation'
28
30
  require 'puppet/pops/issue_reporter'
29
31
  require 'puppet/pops/lookup'
32
+ require 'puppet/pops/lookup/interpolation'
30
33
  require 'puppet/pops/lookup/invocation'
34
+ require 'puppet/pops/lookup/sub_lookup'
31
35
  require 'puppet/pops/lookup/explainer'
32
36
 
33
37
  require 'puppet/pops/model/model'
34
38
 
35
39
  # (the Types module initializes itself)
36
40
  require 'puppet/pops/types/types'
41
+ require 'puppet/pops/types/string_converter'
37
42
 
38
43
  require 'puppet/pops/merge_strategy'
39
44
 
@@ -77,6 +82,7 @@ module Puppet
77
82
  end
78
83
 
79
84
  module Evaluator
85
+ require 'puppet/pops/evaluator/literal_evaluator'
80
86
  require 'puppet/pops/evaluator/callable_signature'
81
87
  require 'puppet/pops/evaluator/runtime3_converter'
82
88
  require 'puppet/pops/evaluator/runtime3_support'
@@ -103,7 +103,11 @@ module Adapters
103
103
  # Produces an URI with path?line=n&pos=n. If origin is unknown the URI is string:?line=n&pos=n
104
104
  def to_uri
105
105
  f = locator.file
106
- f = 'string:' if f.nil? || f.empty?
106
+ if f.nil? || f.empty?
107
+ f = 'string:'
108
+ else
109
+ f = Puppet::Util.path_to_uri(f).to_s
110
+ end
107
111
  URI("#{f}?line=#{line.to_s}&pos=#{pos.to_s}")
108
112
  end
109
113
  end
@@ -154,6 +154,12 @@ class AccessOperator
154
154
  Types::TypeFactory.variant(*keys)
155
155
  end
156
156
 
157
+ def access_PSemVerType(o, scope, keys)
158
+ keys.flatten!
159
+ assert_keys(keys, o, 1, Float::INFINITY, String, Semantic::VersionRange)
160
+ Types::TypeFactory.sem_ver(*keys)
161
+ end
162
+
157
163
  def access_PTupleType(o, scope, keys)
158
164
  keys.flatten!
159
165
  if Types::TypeFactory.is_range_parameter?(keys[-2]) && Types::TypeFactory.is_range_parameter?(keys[-1])
@@ -260,6 +266,15 @@ class AccessOperator
260
266
  end
261
267
  end
262
268
 
269
+ def access_PObjectType(o, scope, keys)
270
+ keys.flatten!
271
+ if keys.size == 1
272
+ Types::TypeFactory.object(keys[0])
273
+ else
274
+ fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Object-Type', :min => 1, :actual => keys.size})
275
+ end
276
+ end
277
+
263
278
  def access_PNotUndefType(o, scope, keys)
264
279
  keys.flatten!
265
280
  case keys.size
@@ -357,6 +372,10 @@ class AccessOperator
357
372
  #
358
373
  def access_PHashType(o, scope, keys)
359
374
  keys.flatten!
375
+ if keys.size == 2 && keys[0].is_a?(Integer) && keys[1].is_a?(Integer)
376
+ return Types::PHashType.new(nil, nil, Types::PIntegerType.new(*keys))
377
+ end
378
+
360
379
  keys[0,2].each_with_index do |k, index|
361
380
  unless k.is_a?(Types::PAnyType)
362
381
  fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash-Type', :actual => k.class})
@@ -364,18 +383,18 @@ class AccessOperator
364
383
  end
365
384
  case keys.size
366
385
  when 2
367
- Types::PHashType.new(keys[0], keys[1], nil)
386
+ size_t = nil
368
387
  when 3
369
388
  size_t = keys[2]
370
389
  size_t = Types::PIntegerType.new(size_t) unless size_t.is_a?(Types::PIntegerType)
371
- Types::PHashType.new(keys[0], keys[1], size_t)
372
390
  when 4
373
- Types::PHashType.new(keys[0], keys[1], collection_size_t(1, keys[2], keys[3]))
391
+ size_t = collection_size_t(2, keys[2], keys[3])
374
392
  else
375
393
  fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {
376
394
  :base_type => 'Hash-Type', :min => 2, :max => 4, :actual => keys.size
377
395
  })
378
396
  end
397
+ Types::PHashType.new(keys[0], keys[1], size_t)
379
398
  end
380
399
 
381
400
  # CollectionType is parameterized with a range
@@ -383,9 +402,9 @@ class AccessOperator
383
402
  keys.flatten!
384
403
  case keys.size
385
404
  when 1
386
- size_t = collection_size_t(1, keys[0])
405
+ size_t = collection_size_t(0, keys[0])
387
406
  when 2
388
- size_t = collection_size_t(1, keys[0], keys[1])
407
+ size_t = collection_size_t(0, keys[0], keys[1])
389
408
  else
390
409
  fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic,
391
410
  {:base_type => 'Collection-Type', :min => 1, :max => 2, :actual => keys.size})
@@ -399,19 +418,31 @@ class AccessOperator
399
418
  keys.flatten!
400
419
  case keys.size
401
420
  when 1
421
+ unless keys[0].is_a?(Types::PAnyType)
422
+ fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class})
423
+ end
424
+ type = keys[0]
402
425
  size_t = nil
403
426
  when 2
404
- size_t = collection_size_t(1, keys[1])
427
+ if keys[0].is_a?(Types::PAnyType)
428
+ size_t = collection_size_t(1, keys[1])
429
+ type = keys[0]
430
+ else
431
+ size_t = collection_size_t(0, keys[0], keys[1])
432
+ type = nil
433
+ end
405
434
  when 3
406
- size_t = collection_size_t(1, keys[1], keys[2])
435
+ if keys[0].is_a?(Types::PAnyType)
436
+ size_t = collection_size_t(1, keys[1], keys[2])
437
+ type = keys[0]
438
+ else
439
+ fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class})
440
+ end
407
441
  else
408
442
  fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic,
409
443
  {:base_type => 'Array-Type', :min => 1, :max => 3, :actual => keys.size})
410
444
  end
411
- unless keys[0].is_a?(Types::PAnyType)
412
- fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class})
413
- end
414
- Types::PArrayType.new(keys[0], size_t)
445
+ Types::PArrayType.new(type, size_t)
415
446
  end
416
447
 
417
448
  # Produces an PIntegerType (range) given one or two keys.
@@ -438,6 +469,11 @@ class AccessOperator
438
469
  access(t, scope, *keys)
439
470
  end
440
471
 
472
+ # If a type reference is encountered here, it's an error
473
+ def access_PTypeReferenceType(o, scope, keys)
474
+ fail(Issues::UNKNOWN_RESOURCE_TYPE, @semantic, {:type_name => o.type_string })
475
+ end
476
+
441
477
  # A Resource can create a new more specific Resource type, and/or an array of resource types
442
478
  # If the given type has title set, it can not be specified further.
443
479
  # @example
@@ -461,12 +497,12 @@ class AccessOperator
461
497
  # Must know which concrete resource type to operate on in all cases.
462
498
  # It is not allowed to specify the type in an array arg - e.g. Resource[[File, 'foo']]
463
499
  # type_name is LHS type_name if set, else the first given arg
464
- type_name = o.type_name || keys.shift
500
+ type_name = o.type_name || Types::TypeFormatter.singleton.capitalize_segments(keys.shift)
465
501
  type_name = case type_name
466
502
  when Types::PResourceType
467
503
  type_name.type_name
468
504
  when String
469
- type_name.downcase
505
+ type_name
470
506
  else
471
507
  # blame given left expression if it defined the type, else the first given key expression
472
508
  blame = o.type_name.nil? ? @semantic.keys[0] : @semantic.left_expr
@@ -474,7 +510,7 @@ class AccessOperator
474
510
  end
475
511
 
476
512
  # type name must conform
477
- if type_name !~ Patterns::CLASSREF
513
+ if type_name !~ Patterns::CLASSREF_EXT
478
514
  fail(Issues::ILLEGAL_CLASSREF, blamed, {:name=>type_name})
479
515
  end
480
516
 
@@ -565,6 +601,8 @@ class AccessOperator
565
601
  c.type_name
566
602
  elsif c.is_a?(String)
567
603
  c.downcase
604
+ elsif c.is_a?(Types::PTypeReferenceType)
605
+ c.type_string.downcase
568
606
  else
569
607
  fail(Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys[i], {:name => c})
570
608
  end
@@ -21,7 +21,6 @@ class CompareOperator
21
21
  @@compare_visitor ||= Visitor.new(self, "cmp", 1, 1)
22
22
  @@match_visitor ||= Visitor.new(self, "match", 2, 2)
23
23
  @@include_visitor ||= Visitor.new(self, "include", 2, 2)
24
- @type_calculator = Types::TypeCalculator.new()
25
24
  end
26
25
 
27
26
  def equals (a, b)
@@ -93,8 +92,13 @@ class CompareOperator
93
92
  end
94
93
  end
95
94
 
95
+ def cmp_Version(a, b)
96
+ raise ArgumentError.new('Versions not comparable to non Versions') unless b.is_a?(Semantic::Version)
97
+ a <=> b
98
+ end
99
+
96
100
  def cmp_Object(a, b)
97
- raise ArgumentError.new("Only Strings and Numbers are comparable")
101
+ raise ArgumentError.new('Only Strings, Numbers, and Versions are comparable')
98
102
  end
99
103
 
100
104
 
@@ -145,8 +149,10 @@ class CompareOperator
145
149
  # Always set match data, a "not found" should not keep old match data visible
146
150
  set_match_data(matched, scope) # creates ephemeral
147
151
  return !!matched
152
+ when String, Semantic::Version
153
+ a.any? { |element| match(b, element, scope) }
148
154
  when Types::PAnyType
149
- a.each {|element| return true if @type_calculator.instance?(b, element) }
155
+ a.each {|element| return true if b.instance?(element) }
150
156
  return false
151
157
  else
152
158
  a.each {|element| return true if equals(element, b) }
@@ -158,6 +164,10 @@ class CompareOperator
158
164
  include?(a.keys, b, scope)
159
165
  end
160
166
 
167
+ def include_VersionRange(a, b, scope)
168
+ Types::PSemVerRangeType.include?(a, b)
169
+ end
170
+
161
171
  # Matches in general by using == operator
162
172
  def match_Object(pattern, a, scope)
163
173
  equals(a, pattern)
@@ -171,12 +181,32 @@ class CompareOperator
171
181
  !!matched # convert to boolean
172
182
  end
173
183
 
184
+ # Matches against semvers and strings
185
+ def match_Version(version, left, scope)
186
+ if left.is_a?(Semantic::Version)
187
+ version == left
188
+ elsif left.is_a? String
189
+ begin
190
+ version == Semantic::Version.parse(left)
191
+ rescue ArgumentError
192
+ false
193
+ end
194
+ else
195
+ false
196
+ end
197
+ end
198
+
199
+ # Matches against semvers and strings
200
+ def match_VersionRange(range, left, scope)
201
+ Types::PSemVerRangeType.include?(range, left)
202
+ end
203
+
174
204
  def match_PAnyType(any_type, left, scope)
175
205
  # right is a type and left is not - check if left is an instance of the given type
176
206
  # (The reverse is not terribly meaningful - computing which of the case options that first produces
177
207
  # an instance of a given type).
178
208
  #
179
- @type_calculator.instance?(any_type, left)
209
+ any_type.instance?(left)
180
210
  end
181
211
 
182
212
  def match_Array(array, left, scope)
@@ -77,7 +77,7 @@ class EvaluatorImpl
77
77
  @@eval_visitor.visit_this_1(self, target, scope)
78
78
 
79
79
  rescue SemanticError => e
80
- # A raised issue may not know the semantic target, use errors call stack, but fill in the
80
+ # A raised issue may not know the semantic target, use errors call stack, but fill in the
81
81
  # rest from a supplied semantic object, or the target instruction if there is not semantic
82
82
  # object.
83
83
  #
@@ -223,6 +223,28 @@ class EvaluatorImpl
223
223
  values.fetch(lval) {|k| fail(Issues::MISSING_MULTI_ASSIGNMENT_KEY, o, :key =>k)},
224
224
  o, scope)
225
225
  end
226
+ elsif values.is_a?(Puppet::Pops::Types::PHostClassType)
227
+ # assign variables from class variables
228
+ # lookup class resource and return one or more parameter values
229
+ # TODO: behavior when class_name is nil
230
+ resource = find_resource(scope, 'class', values.class_name)
231
+ if resource
232
+ base_name = "#{values.class_name.downcase}::"
233
+ idx = -1
234
+ result = lvalues.map do |lval|
235
+ idx += 1
236
+ varname = "#{base_name}#{lval}"
237
+ if variable_exists?(varname, scope)
238
+ result = get_variable_value(varname, o, scope)
239
+ assign(lval, result, o, scope)
240
+ else
241
+ fail(Puppet::Pops::Issues::MISSING_MULTI_ASSIGNMENT_VARIABLE, o.left_expr.values[idx], {:name => varname})
242
+ end
243
+ end
244
+ else
245
+ fail(Issues::UNKNOWN_RESOURCE, o.right_expr, {:type_name => 'Class', :title => values.class_name})
246
+ end
247
+
226
248
  else
227
249
  values = [values] unless values.is_a?(Array)
228
250
  if values.size != lvalues.size
@@ -278,7 +300,9 @@ class EvaluatorImpl
278
300
  # A QualifiedReference (i.e. a capitalized qualified name such as Foo, or Foo::Bar) evaluates to a PType
279
301
  #
280
302
  def eval_QualifiedReference(o, scope)
281
- @@type_parser.interpret(o, scope)
303
+ type = @@type_parser.interpret(o, scope)
304
+ fail(Issues::UNKNOWN_RESOURCE_TYPE, o, {:type_name => type.type_string }) if type.is_a?(Types::PTypeReferenceType)
305
+ type
282
306
  end
283
307
 
284
308
  def eval_NotExpression(o, scope)
@@ -424,7 +448,15 @@ class EvaluatorImpl
424
448
  #
425
449
  def eval_AccessExpression(o, scope)
426
450
  left = evaluate(o.left_expr, scope)
427
- keys = o.keys.nil? ? [] : o.keys.collect {|key| evaluate(key, scope) }
451
+ keys = o.keys || []
452
+ if left.is_a?(Types::PHostClassType)
453
+ # Evaluate qualified references without errors no undefined types
454
+ keys = keys.map {|key| key.is_a?(Model::QualifiedReference) ? @@type_parser.interpret(key, scope) : evaluate(key, scope) }
455
+ else
456
+ keys = keys.map {|key| evaluate(key, scope) }
457
+ # Resource[File] becomes File
458
+ return keys[0] if Types::PResourceType::DEFAULT == left && keys.size == 1 && keys[0].is_a?(Types::PResourceType)
459
+ end
428
460
  AccessOperator.new(o).access(left, scope, *keys)
429
461
  end
430
462
 
@@ -511,11 +543,17 @@ class EvaluatorImpl
511
543
  # matches RHS types as instance of for all types except a parameterized Regexp[R]
512
544
  if pattern.is_a?(Types::PAnyType)
513
545
  # evaluate as instance? of type check
514
- matched = @@type_calculator.instance?(pattern, left)
546
+ matched = pattern.instance?(left)
515
547
  # convert match result to Boolean true, or false
516
548
  return o.operator == :'=~' ? !!matched : !matched
517
549
  end
518
550
 
551
+ if pattern.is_a?(Semantic::VersionRange)
552
+ # evaluate if range includes version
553
+ matched = Types::PSemVerRangeType.include?(pattern, left)
554
+ return o.operator == :'=~' ? matched : !matched
555
+ end
556
+
519
557
  begin
520
558
  pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)
521
559
  rescue StandardError => e
@@ -692,6 +730,9 @@ class EvaluatorImpl
692
730
  end
693
731
  evaluated_name.type_name # assume validated
694
732
 
733
+ when Types::PTypeReferenceType
734
+ fail(Issues::UNKNOWN_RESOURCE_TYPE, o.type_string, {:type_name => evaluated_name.to_s})
735
+
695
736
  else
696
737
  actual = type_calculator.generalize(type_calculator.infer(evaluated_name)).to_s
697
738
  fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, {:actual=>actual})
@@ -814,9 +855,20 @@ class EvaluatorImpl
814
855
  # Evaluates function call by name.
815
856
  #
816
857
  def eval_CallNamedFunctionExpression(o, scope)
858
+ # If LHS is a type (i.e. Integer, or Integer[...]
859
+ # the call is taken as an instantiation of the given type
860
+ #
861
+ functor = o.functor_expr
862
+ if functor.is_a?(Model::QualifiedReference) ||
863
+ functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference)
864
+ # instantiation
865
+ type = evaluate(functor, scope)
866
+ return call_function_with_block('new', unfold([type], o.arguments || [], scope), o, scope)
867
+ end
868
+
817
869
  # The functor expression is not evaluated, it is not possible to select the function to call
818
870
  # via an expression like $a()
819
- case o.functor_expr
871
+ case functor
820
872
  when Model::QualifiedName
821
873
  # ok
822
874
  when Model::RenderStringExpression
@@ -841,6 +893,17 @@ class EvaluatorImpl
841
893
  fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o})
842
894
  end
843
895
  name = name.value # the string function name
896
+
897
+ obj = receiver[0]
898
+ receiver_type = Types::TypeCalculator.infer(obj)
899
+ if receiver_type.is_a?(Types::PObjectType)
900
+ member = receiver_type[name]
901
+ unless member.nil?
902
+ args = unfold([], o.arguments || [], scope)
903
+ return o.lambda.nil? ? member.invoke(obj, scope, args) : member.invoke(obj, scope, args, &proc_from_lambda(o.lambda, scope))
904
+ end
905
+ end
906
+
844
907
  call_function_with_block(name, unfold(receiver, o.arguments || [], scope), o, scope)
845
908
  end
846
909
 
@@ -848,12 +911,17 @@ class EvaluatorImpl
848
911
  if o.lambda.nil?
849
912
  call_function(name, evaluated_arguments, o, scope)
850
913
  else
851
- closure = Closure.new(self, o.lambda, scope)
852
- call_function(name, evaluated_arguments, o, scope, &PuppetProc.new(closure) { |*args| closure.call(*args) })
914
+ call_function(name, evaluated_arguments, o, scope, &proc_from_lambda(o.lambda, scope))
853
915
  end
854
916
  end
855
917
  private :call_function_with_block
856
918
 
919
+ def proc_from_lambda(lambda, scope)
920
+ closure = Closure.new(self, lambda, scope)
921
+ PuppetProc.new(closure) { |*args| closure.call(*args) }
922
+ end
923
+ private :proc_from_lambda
924
+
857
925
  # @example
858
926
  # $x ? { 10 => true, 20 => false, default => 0 }
859
927
  #
@@ -1117,21 +1185,6 @@ class EvaluatorImpl
1117
1185
  # of value except regular expression where a match is performed.
1118
1186
  #
1119
1187
  def is_match?(left, right, o, option_expr, scope)
1120
- if right.is_a?(Regexp)
1121
- return false unless left.is_a? String
1122
- matched = right.match(left)
1123
- set_match_data(matched, scope) # creates or clears ephemeral
1124
- !!matched # convert to boolean
1125
- elsif right.is_a?(Types::PAnyType)
1126
- # right is a type and left is not - check if left is an instance of the given type
1127
- # (The reverse is not terribly meaningful - computing which of the case options that first produces
1128
- # an instance of a given type).
1129
- #
1130
- @@type_calculator.instance?(right, left)
1131
- else
1132
- # Handle equality the same way as the language '==' operator (case insensitive etc.)
1133
- @@compare_operator.equals(left,right)
1134
- end
1135
1188
  @@compare_operator.match(left, right, scope)
1136
1189
  end
1137
1190