puppet 4.0.0 → 4.1.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/ext/build_defaults.yaml +8 -35
  3. data/ext/debian/puppet.default +0 -5
  4. data/ext/debian/puppet.init +1 -15
  5. data/lib/hiera/puppet_function.rb +15 -4
  6. data/lib/puppet/application/agent.rb +5 -0
  7. data/lib/puppet/application/apply.rb +23 -2
  8. data/lib/puppet/application/device.rb +8 -3
  9. data/lib/puppet/application/master.rb +16 -5
  10. data/lib/puppet/configurer.rb +7 -5
  11. data/lib/puppet/defaults.rb +18 -0
  12. data/lib/puppet/environments.rb +1 -1
  13. data/lib/puppet/error.rb +27 -1
  14. data/lib/puppet/file_serving/metadata.rb +13 -8
  15. data/lib/puppet/file_serving/terminus_helper.rb +7 -8
  16. data/lib/puppet/file_system.rb +13 -0
  17. data/lib/puppet/file_system/file_impl.rb +4 -0
  18. data/lib/puppet/file_system/memory_impl.rb +4 -0
  19. data/lib/puppet/file_system/windows.rb +8 -0
  20. data/lib/puppet/functions.rb +33 -3
  21. data/lib/puppet/functions/defined.rb +130 -0
  22. data/lib/puppet/functions/regsubst.rb +1 -1
  23. data/lib/puppet/functions/split.rb +1 -1
  24. data/lib/puppet/indirector/catalog/compiler.rb +1 -1
  25. data/lib/puppet/indirector/facts/facter.rb +11 -0
  26. data/lib/puppet/loaders.rb +1 -0
  27. data/lib/puppet/node.rb +17 -1
  28. data/lib/puppet/node/environment.rb +4 -0
  29. data/lib/puppet/parser/ast/pops_bridge.rb +4 -0
  30. data/lib/puppet/parser/compiler.rb +9 -0
  31. data/lib/puppet/parser/functions/defined.rb +25 -1
  32. data/lib/puppet/parser/functions/file.rb +3 -1
  33. data/lib/puppet/parser/scope.rb +11 -2
  34. data/lib/puppet/parser/templatewrapper.rb +2 -1
  35. data/lib/puppet/pops.rb +4 -0
  36. data/lib/puppet/pops/evaluator/access_operator.rb +25 -5
  37. data/lib/puppet/pops/evaluator/closure.rb +28 -2
  38. data/lib/puppet/pops/evaluator/collector_transformer.rb +1 -11
  39. data/lib/puppet/pops/evaluator/collectors/catalog_collector.rb +4 -0
  40. data/lib/puppet/pops/evaluator/collectors/exported_collector.rb +4 -0
  41. data/lib/puppet/pops/evaluator/compare_operator.rb +43 -0
  42. data/lib/puppet/pops/evaluator/epp_evaluator.rb +7 -2
  43. data/lib/puppet/pops/evaluator/evaluator_impl.rb +48 -14
  44. data/lib/puppet/pops/evaluator/runtime3_support.rb +10 -5
  45. data/lib/puppet/pops/functions/dispatch.rb +6 -1
  46. data/lib/puppet/pops/functions/dispatcher.rb +7 -1
  47. data/lib/puppet/pops/issue_reporter.rb +42 -16
  48. data/lib/puppet/pops/issues.rb +116 -2
  49. data/lib/puppet/pops/loader/loader.rb +11 -0
  50. data/lib/puppet/pops/loader/loader_paths.rb +67 -6
  51. data/lib/puppet/pops/loader/module_loaders.rb +19 -8
  52. data/lib/puppet/pops/loader/puppet_function_instantiator.rb +78 -0
  53. data/lib/puppet/pops/loaders.rb +6 -4
  54. data/lib/puppet/pops/migration/migration_checker.rb +54 -0
  55. data/lib/puppet/pops/model/factory.rb +5 -1
  56. data/lib/puppet/pops/model/model_label_provider.rb +2 -0
  57. data/lib/puppet/pops/model/model_meta.rb +5 -1
  58. data/lib/puppet/pops/parser/egrammar.ra +9 -10
  59. data/lib/puppet/pops/parser/eparser.rb +1061 -1047
  60. data/lib/puppet/pops/parser/epp_support.rb +18 -9
  61. data/lib/puppet/pops/parser/evaluating_parser.rb +7 -1
  62. data/lib/puppet/pops/parser/heredoc_support.rb +12 -11
  63. data/lib/puppet/pops/parser/interpolation_support.rb +7 -1
  64. data/lib/puppet/pops/parser/lexer2.rb +29 -12
  65. data/lib/puppet/pops/parser/lexer_support.rb +52 -23
  66. data/lib/puppet/pops/parser/parser_support.rb +11 -14
  67. data/lib/puppet/pops/parser/slurp_support.rb +22 -6
  68. data/lib/puppet/pops/types/type_calculator.rb +156 -55
  69. data/lib/puppet/pops/types/type_factory.rb +66 -13
  70. data/lib/puppet/pops/types/type_parser.rb +22 -13
  71. data/lib/puppet/pops/types/types.rb +23 -4
  72. data/lib/puppet/pops/types/types_meta.rb +13 -2
  73. data/lib/puppet/pops/validation.rb +25 -2
  74. data/lib/puppet/pops/validation/checker4_0.rb +63 -31
  75. data/lib/puppet/provider/group/windows_adsi.rb +8 -4
  76. data/lib/puppet/provider/mount/parsed.rb +145 -2
  77. data/lib/puppet/provider/package/apt.rb +1 -1
  78. data/lib/puppet/provider/package/pip.rb +11 -2
  79. data/lib/puppet/provider/package/pkgng.rb +134 -0
  80. data/lib/puppet/provider/package/portage.rb +1 -1
  81. data/lib/puppet/provider/package/ports.rb +0 -3
  82. data/lib/puppet/provider/package/windows/exe_package.rb +0 -1
  83. data/lib/puppet/provider/package/windows/msi_package.rb +0 -1
  84. data/lib/puppet/provider/package/zypper.rb +50 -15
  85. data/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb +32 -7
  86. data/lib/puppet/provider/service/debian.rb +1 -1
  87. data/lib/puppet/provider/service/init.rb +7 -0
  88. data/lib/puppet/provider/user/openbsd.rb +1 -0
  89. data/lib/puppet/provider/user/windows_adsi.rb +45 -2
  90. data/lib/puppet/reference/indirection.rb +1 -1
  91. data/lib/puppet/resource.rb +1 -1
  92. data/lib/puppet/resource/catalog.rb +0 -4
  93. data/lib/puppet/settings.rb +19 -0
  94. data/lib/puppet/type/file.rb +1 -0
  95. data/lib/puppet/type/file/ensure.rb +1 -1
  96. data/lib/puppet/type/mount.rb +9 -1
  97. data/lib/puppet/type/scheduled_task.rb +13 -0
  98. data/lib/puppet/type/tidy.rb +3 -1
  99. data/lib/puppet/type/user.rb +32 -0
  100. data/lib/puppet/type/yumrepo.rb +5 -5
  101. data/lib/puppet/util/log.rb +50 -8
  102. data/lib/puppet/util/log/destinations.rb +23 -2
  103. data/lib/puppet/util/logging.rb +37 -1
  104. data/lib/puppet/util/run_mode.rb +1 -14
  105. data/lib/puppet/util/windows/adsi.rb +130 -58
  106. data/lib/puppet/version.rb +1 -1
  107. data/man/man5/puppet.conf.5 +48 -6
  108. data/man/man8/extlookup2hiera.8 +1 -1
  109. data/man/man8/puppet-agent.8 +4 -1
  110. data/man/man8/puppet-apply.8 +4 -1
  111. data/man/man8/puppet-ca.8 +1 -1
  112. data/man/man8/puppet-catalog.8 +1 -1
  113. data/man/man8/puppet-cert.8 +1 -1
  114. data/man/man8/puppet-certificate.8 +1 -1
  115. data/man/man8/puppet-certificate_request.8 +1 -1
  116. data/man/man8/puppet-certificate_revocation_list.8 +1 -1
  117. data/man/man8/puppet-config.8 +1 -1
  118. data/man/man8/puppet-describe.8 +1 -1
  119. data/man/man8/puppet-device.8 +6 -3
  120. data/man/man8/puppet-doc.8 +1 -1
  121. data/man/man8/puppet-epp.8 +1 -1
  122. data/man/man8/puppet-facts.8 +1 -1
  123. data/man/man8/puppet-file.8 +1 -1
  124. data/man/man8/puppet-filebucket.8 +1 -1
  125. data/man/man8/puppet-help.8 +1 -1
  126. data/man/man8/puppet-inspect.8 +1 -1
  127. data/man/man8/puppet-key.8 +1 -1
  128. data/man/man8/puppet-man.8 +1 -1
  129. data/man/man8/puppet-master.8 +4 -1
  130. data/man/man8/puppet-module.8 +1 -1
  131. data/man/man8/puppet-node.8 +1 -1
  132. data/man/man8/puppet-parser.8 +1 -1
  133. data/man/man8/puppet-plugin.8 +1 -1
  134. data/man/man8/puppet-report.8 +1 -1
  135. data/man/man8/puppet-resource.8 +1 -1
  136. data/man/man8/puppet-resource_type.8 +1 -1
  137. data/man/man8/puppet-status.8 +1 -1
  138. data/man/man8/puppet.8 +1 -1
  139. data/spec/fixtures/unit/data_providers/environments/production/lib/puppet/functions/environment/data.rb +3 -1
  140. data/spec/fixtures/unit/data_providers/environments/production/modules/xyz/functions/data.pp +6 -0
  141. data/spec/fixtures/unit/data_providers/environments/production/modules/xyz/lib/puppet/bindings/xyz/default.rb +9 -0
  142. data/spec/fixtures/unit/data_providers/environments/production/modules/xyz/manifests/init.pp +9 -0
  143. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/functions/puppetcalled.pp +3 -0
  144. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/functions/puppetcaller.pp +3 -0
  145. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/functions/puppetcaller4.pp +3 -0
  146. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/lib/puppet/functions/user/callingpuppet.rb +5 -0
  147. data/spec/fixtures/unit/pops/loaders/loaders/module_no_lib/modules/modulea/functions/hello.pp +3 -0
  148. data/spec/fixtures/unit/pops/loaders/loaders/module_no_lib/modules/modulea/manifests/init.pp +3 -0
  149. data/spec/fixtures/unit/pops/loaders/loaders/module_no_lib/modules/modulea/metadata.json +10 -0
  150. data/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/hello.pp +3 -0
  151. data/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/subspace/hello.pp +3 -0
  152. data/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/metadata.json +1 -10
  153. data/spec/fixtures/unit/provider/mount/parsed/aix.filesystems +93 -85
  154. data/spec/fixtures/unit/provider/mount/parsed/aix.mount +11 -7
  155. data/spec/fixtures/unit/provider/package/pkgng/pkg.info +8 -0
  156. data/spec/fixtures/unit/provider/package/pkgng/pkg.query +1 -0
  157. data/spec/fixtures/unit/provider/package/pkgng/pkg.query_absent +1 -0
  158. data/spec/fixtures/unit/provider/package/pkgng/pkg.version +3 -0
  159. data/spec/fixtures/unit/provider/package/zypper/zypper-list-updates-empty.out +3 -0
  160. data/spec/integration/application/apply_spec.rb +49 -0
  161. data/spec/integration/faces/plugin_spec.rb +0 -4
  162. data/spec/integration/indirector/facts/facter_spec.rb +59 -0
  163. data/spec/integration/parser/compiler_spec.rb +850 -0
  164. data/spec/integration/parser/resource_expressions_spec.rb +3 -0
  165. data/spec/integration/parser/scope_spec.rb +26 -5
  166. data/spec/integration/transaction_spec.rb +1 -1
  167. data/spec/integration/type/file_spec.rb +318 -41
  168. data/spec/integration/util/windows/security_spec.rb +14 -5
  169. data/spec/lib/matchers/resource.rb +22 -1
  170. data/spec/lib/puppet_spec/matchers.rb +6 -4
  171. data/spec/unit/application/master_spec.rb +33 -7
  172. data/spec/unit/data_providers/function_data_provider_spec.rb +10 -1
  173. data/spec/unit/file_serving/metadata_spec.rb +1 -1
  174. data/spec/unit/file_serving/terminus_helper_spec.rb +2 -3
  175. data/spec/unit/file_system_spec.rb +38 -0
  176. data/spec/unit/functions/defined_spec.rb +289 -0
  177. data/spec/unit/functions/hiera_spec.rb +8 -6
  178. data/spec/unit/functions/regsubst_spec.rb +4 -0
  179. data/spec/unit/functions/split_spec.rb +8 -0
  180. data/spec/unit/functions4_spec.rb +97 -2
  181. data/spec/unit/indirector/facts/facter_spec.rb +7 -0
  182. data/spec/unit/node_spec.rb +6 -0
  183. data/spec/unit/parser/functions/file_spec.rb +7 -1
  184. data/spec/unit/parser/functions/template_spec.rb +1 -1
  185. data/spec/unit/parser/scope_spec.rb +2 -2
  186. data/spec/unit/parser/templatewrapper_spec.rb +1 -1
  187. data/spec/unit/pops/evaluator/access_ops_spec.rb +19 -0
  188. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +84 -18
  189. data/spec/unit/pops/evaluator/variables_spec.rb +1 -1
  190. data/spec/unit/pops/issues_spec.rb +16 -16
  191. data/spec/unit/pops/loaders/loaders_spec.rb +106 -48
  192. data/spec/unit/pops/migration_spec.rb +53 -0
  193. data/spec/unit/pops/parser/lexer2_spec.rb +142 -1
  194. data/spec/unit/pops/parser/parse_heredoc_spec.rb +26 -0
  195. data/spec/unit/pops/types/type_calculator_spec.rb +205 -12
  196. data/spec/unit/pops/validation_spec.rb +66 -0
  197. data/spec/unit/pops/validator/validator_spec.rb +1 -1
  198. data/spec/unit/provider/group/windows_adsi_spec.rb +57 -9
  199. data/spec/unit/provider/mount/parsed_spec.rb +31 -5
  200. data/spec/unit/provider/package/apt_spec.rb +5 -0
  201. data/spec/unit/provider/package/pip_spec.rb +9 -0
  202. data/spec/unit/provider/package/pkgng_spec.rb +172 -0
  203. data/spec/unit/provider/package/windows/exe_package_spec.rb +0 -1
  204. data/spec/unit/provider/package/windows/msi_package_spec.rb +0 -1
  205. data/spec/unit/provider/package/zypper_spec.rb +50 -19
  206. data/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +312 -70
  207. data/spec/unit/provider/service/base_spec.rb +38 -27
  208. data/spec/unit/provider/service/debian_spec.rb +8 -0
  209. data/spec/unit/provider/service/freebsd_spec.rb +1 -0
  210. data/spec/unit/provider/service/gentoo_spec.rb +1 -0
  211. data/spec/unit/provider/service/init_spec.rb +18 -0
  212. data/spec/unit/provider/service/openbsd_spec.rb +1 -0
  213. data/spec/unit/provider/service/redhat_spec.rb +1 -0
  214. data/spec/unit/provider/user/windows_adsi_spec.rb +134 -5
  215. data/spec/unit/settings_spec.rb +11 -0
  216. data/spec/unit/util/log_spec.rb +113 -0
  217. data/spec/unit/util/windows/adsi_spec.rb +135 -41
  218. data/spec/unit/util/windows/sid_spec.rb +0 -10
  219. metadata +48 -2
@@ -7,10 +7,13 @@
7
7
  # TODO: More detailed performance analysis of excessive character escaping and interpolation.
8
8
  #
9
9
  module Puppet::Pops::Parser::SlurpSupport
10
+ include Puppet::Pops::Parser::LexerSupport
10
11
 
11
12
  SLURP_SQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*[']/
12
13
  SLURP_DQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*(["]|[$]\{?)/
13
14
  SLURP_UQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*([$]\{?|\z)/
15
+ # unquoted, no escapes
16
+ SLURP_UQNE_PATTERN = /(\$\{?|\z)/m
14
17
  SLURP_ALL_PATTERN = /.*(\z)/m
15
18
  SQ_ESCAPES = %w{ \\ ' }
16
19
  DQ_ESCAPES = %w{ \\ $ ' " r n t s u}+["\r\n", "\n"]
@@ -19,7 +22,8 @@ module Puppet::Pops::Parser::SlurpSupport
19
22
  def slurp_sqstring
20
23
  # skip the leading '
21
24
  @scanner.pos += 1
22
- str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes) || lex_error("Unclosed quote after \"'\" followed by '#{followed_by}'")
25
+ str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes)
26
+ lex_error(Puppet::Pops::Issues::UNCLOSED_QUOTE, :after => "\"'\"", :followed_by => followed_by) unless str
23
27
  str[0..-2] # strip closing "'" from result
24
28
  end
25
29
 
@@ -28,7 +32,7 @@ module Puppet::Pops::Parser::SlurpSupport
28
32
  last = scn.matched
29
33
  str = slurp(scn, SLURP_DQ_PATTERN, DQ_ESCAPES, false)
30
34
  unless str
31
- lex_error("Unclosed quote after #{format_quote(last)} followed by '#{followed_by}'")
35
+ lex_error(Puppet::Pops::Issues::UNCLOSED_QUOTE, :after => format_quote(last), :followed_by => followed_by)
32
36
  end
33
37
 
34
38
  # Terminator may be a single char '"', '$', or two characters '${' group match 1 (scn[1]) from the last slurp holds this
@@ -41,6 +45,7 @@ module Puppet::Pops::Parser::SlurpSupport
41
45
  scn = @scanner
42
46
  last = scn.matched
43
47
  ignore = true
48
+
44
49
  str = slurp(scn, @lexing_context[:uq_slurp_pattern], @lexing_context[:escapes], :ignore_invalid_escapes)
45
50
 
46
51
  # Terminator may be a single char '$', two characters '${', or empty string '' at the end of intput.
@@ -65,11 +70,12 @@ module Puppet::Pops::Parser::SlurpSupport
65
70
  # Process unicode escapes first as they require getting 4 hex digits
66
71
  # If later a \u is found it is warned not to be a unicode escape
67
72
  if escapes.include?('u')
68
- str.gsub!(/\\u([\da-fA-F]{4})/m) {
69
- [$1.hex].pack("U")
73
+ str.gsub!(/\\u(?:([\da-fA-F]{4})|\{([\da-fA-F]{1,6})\})/m) {
74
+ [($1 || $2).hex].pack("U")
70
75
  }
71
76
  end
72
77
 
78
+ begin
73
79
  str.gsub!(/\\([^\r\n]|(?:\r?\n))/m) {
74
80
  ch = $1
75
81
  if escapes.include? ch
@@ -79,17 +85,27 @@ module Puppet::Pops::Parser::SlurpSupport
79
85
  when 't' ; "\t"
80
86
  when 's' ; " "
81
87
  when 'u'
82
- Puppet.warning(positioned_message("Unicode escape '\\u' was not followed by 4 hex digits"))
88
+ lex_warning(Puppet::Pops::Issues::ILLEGAL_UNICODE_ESCAPE)
83
89
  "\\u"
84
90
  when "\n" ; ''
85
91
  when "\r\n"; ''
86
92
  else ch
87
93
  end
88
94
  else
89
- Puppet.warning(positioned_message("Unrecognized escape sequence '\\#{ch}'")) unless ignore_invalid_escapes
95
+ lex_warning(Puppet::Pops::Issues::UNRECOGNIZED_ESCAPE, :ch => ch) unless ignore_invalid_escapes
90
96
  "\\#{ch}"
91
97
  end
92
98
  }
99
+ rescue ArgumentError => e
100
+ # A invalid byte sequence may be the result of faulty input as well, but that could not possibly
101
+ # have reached this far... Unfortunately there is no more specific error and a match on message is
102
+ # required to differentiate from other internal problems.
103
+ if e.message =~ /invalid byte sequence/
104
+ lex_error(Puppet::Pops::Issues::ILLEGAL_UNICODE_ESCAPE)
105
+ else
106
+ raise e
107
+ end
108
+ end
93
109
  str
94
110
  end
95
111
  end
@@ -268,14 +268,21 @@ class Puppet::Pops::Types::TypeCalculator
268
268
  if t2.is_a?(Class)
269
269
  t2 = type(t2)
270
270
  end
271
+ t2_class = t2.class
272
+
271
273
  # Unit can be assigned to anything
272
- return true if t2.class == Types::PUnitType
274
+ return true if t2_class == Types::PUnitType
273
275
 
274
- if t2.class == Types::PVariantType
276
+ if t2_class == Types::PVariantType
275
277
  # Assignable if all contained types are assignable
276
278
  t2.types.all? { |vt| @@assignable_visitor.visit_this_1(self, t, vt) }
277
279
  else
278
- @@assignable_visitor.visit_this_1(self, t, t2)
280
+ # Turn NotUndef[T] into T when T is not assignable from Undef
281
+ if t2_class == Types::PNotUndefType && !(t2.type.nil? || assignable?(t2.type, @nil_t))
282
+ assignable?(t, t2.type)
283
+ else
284
+ @@assignable_visitor.visit_this_1(self, t, t2)
285
+ end
279
286
  end
280
287
  end
281
288
 
@@ -353,10 +360,23 @@ class Puppet::Pops::Types::TypeCalculator
353
360
  # do nothing, there is nothing to change for most types
354
361
  end
355
362
 
363
+ # @return [Boolean] true if the given argument is contained in a struct element key
364
+ def is_struct_element_key?(o)
365
+ c = o.eContainer
366
+ if c.is_a?(Types::POptionalType)
367
+ o = c
368
+ c = c.eContainer
369
+ end
370
+ c.is_a?(Types::PStructElement) && c.key_type.equal?(o)
371
+ end
372
+ private :is_struct_element_key?
373
+
356
374
  def generalize_PStringType(o)
357
- o.values = []
358
- o.size_type = nil
359
- []
375
+ # Skip generalization if the string is contained in a PStructElement key.
376
+ unless is_struct_element_key?(o)
377
+ o.values = []
378
+ o.size_type = nil
379
+ end
360
380
  end
361
381
 
362
382
  def generalize_PCollectionType(o)
@@ -433,8 +453,13 @@ class Puppet::Pops::Types::TypeCalculator
433
453
  def instance_of_PStringType(t, o)
434
454
  return false unless o.is_a?(String)
435
455
  # true if size compliant
436
- size_t = t.size_type || @collection_default_size_t
437
- instance_of_PIntegerType(size_t, o.size)
456
+ size_t = t.size_type
457
+ if size_t.nil? || instance_of_PIntegerType(size_t, o.size)
458
+ values = t.values
459
+ values.empty? || values.include?(o)
460
+ else
461
+ false
462
+ end
438
463
  end
439
464
 
440
465
  def instance_of_PTupleType(t, o)
@@ -451,9 +476,18 @@ class Puppet::Pops::Types::TypeCalculator
451
476
 
452
477
  def instance_of_PStructType(t, o)
453
478
  return false unless o.is_a?(Hash)
454
- h = t.hashed_elements
455
- # all keys must be present and have a value (even if nil/undef)
456
- (o.keys - h.keys).empty? && h.all? { |k,v| instance_of(v, o[k]) }
479
+ matched = 0
480
+ t.elements.all? do |e|
481
+ key = e.name
482
+ v = o[key]
483
+ if v.nil? && !o.include?(key)
484
+ # Entry is missing. Only OK when key is optional
485
+ assignable?(e.key_type, @nil_t)
486
+ else
487
+ matched += 1
488
+ instance_of(e.value_type, v)
489
+ end
490
+ end && matched == o.size
457
491
  end
458
492
 
459
493
  def instance_of_PHashType(t, o)
@@ -470,6 +504,10 @@ class Puppet::Pops::Types::TypeCalculator
470
504
  instance_of(@data_variant_t, o)
471
505
  end
472
506
 
507
+ def instance_of_PNotUndefType(t, o)
508
+ !(o.nil? || o == :undef) && (t.type.nil? || instance_of(t.type, o))
509
+ end
510
+
473
511
  def instance_of_PUndefType(t, o)
474
512
  o.nil? || o == :undef
475
513
  end
@@ -597,7 +635,8 @@ class Puppet::Pops::Types::TypeCalculator
597
635
 
598
636
  if t1.is_a?(Types::PStringType) && t2.is_a?(Types::PStringType)
599
637
  t = Types::PStringType.new()
600
- t.values = t1.values | t2.values
638
+ t.values = t1.values | t2.values unless t1.values.empty? || t2.values.empty?
639
+ t.size_type = common_type(t1.size_type, t2.size_type) unless t1.size_type.nil? || t2.size_type.nil?
601
640
  return t
602
641
  end
603
642
 
@@ -919,27 +958,23 @@ class Puppet::Pops::Types::TypeCalculator
919
958
  type.key_type = Types::PUndefType.new
920
959
  type.element_type = Types::PUndefType.new
921
960
  type.size_type = size_as_type(o)
922
- else
923
- if o.keys.find {|k| !instance_of_PStringType(@non_empty_string_t, k) }
924
- type = Types::PHashType.new
925
- ktype = Types::PVariantType.new
926
- ktype.types = o.keys.map {|k| infer_set(k) }
927
- etype = Types::PVariantType.new
928
- etype.types = o.values.map {|e| infer_set(e) }
929
- type.key_type = unwrap_single_variant(ktype)
930
- type.element_type = unwrap_single_variant(etype)
931
- type.size_type = size_as_type(o)
932
- else
933
- elements = []
934
- o.each_pair do |k,v|
935
- element = Types::PStructElement.new
936
- element.name = k
937
- element.type = infer_set(v)
938
- elements << element
939
- end
940
- type = Types::PStructType.new
941
- type.elements = elements
961
+ elsif o.keys.all? {|k| instance_of_PStringType(@non_empty_string_t, k) }
962
+ type = Types::PStructType.new
963
+ type.elements = o.map do |k,v|
964
+ element = Types::PStructElement.new
965
+ element.key_type = infer_String(k)
966
+ element.value_type = infer_set(v)
967
+ element
942
968
  end
969
+ else
970
+ type = Types::PHashType.new
971
+ ktype = Types::PVariantType.new
972
+ ktype.types = o.keys.map {|k| infer_set(k) }
973
+ etype = Types::PVariantType.new
974
+ etype.types = o.values.map {|e| infer_set(e) }
975
+ type.key_type = unwrap_single_variant(ktype)
976
+ type.element_type = unwrap_single_variant(etype)
977
+ type.size_type = size_as_type(o)
943
978
  end
944
979
  type
945
980
  end
@@ -963,6 +998,11 @@ class Puppet::Pops::Types::TypeCalculator
963
998
  t2.is_a?(Types::PAnyType)
964
999
  end
965
1000
 
1001
+ # @api private
1002
+ def assignable_PNotUndefType(t, t2)
1003
+ !assignable?(t2, @nil_t) && (t.type.nil? || assignable?(t.type, t2))
1004
+ end
1005
+
966
1006
  # @api private
967
1007
  def assignable_PUndefType(t, t2)
968
1008
  # Only undef/nil is assignable to nil type
@@ -1103,6 +1143,17 @@ class Puppet::Pops::Types::TypeCalculator
1103
1143
  end
1104
1144
  end
1105
1145
 
1146
+ # @api private
1147
+ def self.is_kind_of_optional?(t, optional = true)
1148
+ case t
1149
+ when Types::POptionalType
1150
+ true
1151
+ when Types::PVariantType
1152
+ t.types.all? {|t2| is_kind_of_optional?(t2, optional) }
1153
+ else
1154
+ false
1155
+ end
1156
+ end
1106
1157
 
1107
1158
  def callable_PArrayType(args_array, callable_t)
1108
1159
  return false unless assignable?(callable_t.param_types, args_array)
@@ -1186,22 +1237,34 @@ class Puppet::Pops::Types::TypeCalculator
1186
1237
  #
1187
1238
  def assignable_PStructType(t, t2)
1188
1239
  if t2.is_a?(Types::PStructType)
1189
- h = t.hashed_elements
1190
1240
  h2 = t2.hashed_elements
1191
- (h2.keys - h.keys).empty? && h.all? {|k, v| v2 = h2[k]; assignable?(v, v2.nil? ? @nil_t : v2) }
1241
+ matched = 0
1242
+ t.elements.all? do |e1|
1243
+ e2 = h2[e1.name]
1244
+ if e2.nil?
1245
+ assignable?(e1.key_type, @nil_t)
1246
+ else
1247
+ matched += 1
1248
+ assignable?(e1.key_type, e2.key_type) && assignable?(e1.value_type, e2.value_type)
1249
+ end
1250
+ end && matched == h2.size
1192
1251
  elsif t2.is_a?(Types::PHashType)
1193
- size_t2 = t2.size_type || @collection_default_size_t
1194
- size_t = Types::PIntegerType.new
1195
- elements = t.elements
1196
- size_t.from = elements.count {|e| !assignable?(e.type, @nil_t) }
1197
- size_t.to = elements.size
1198
- # compatible size
1199
- # hash key type must be string of min 1 size
1200
- # hash value t must be assignable to each key
1201
- element_type = t2.element_type
1202
- assignable_PIntegerType(size_t, size_t2) &&
1203
- (size_t2.to == 0 || assignable?(@non_empty_string_t, t2.key_type)) &&
1204
- elements.all? {|e| assignable?(e.type, element_type) }
1252
+ required = 0
1253
+ required_elements_assignable = t.elements.all? do |e|
1254
+ if assignable?(e.key_type, @nil_t)
1255
+ true
1256
+ else
1257
+ required += 1
1258
+ assignable?(e.value_type, t2.element_type)
1259
+ end
1260
+ end
1261
+ if required_elements_assignable
1262
+ size_t2 = t2.size_type || @collection_default_size_t
1263
+ size_t = Types::PIntegerType.new
1264
+ size_t.from = required
1265
+ size_t.to = t.elements.size
1266
+ assignable_PIntegerType(size_t, size_t2)
1267
+ end
1205
1268
  else
1206
1269
  false
1207
1270
  end
@@ -1210,8 +1273,9 @@ class Puppet::Pops::Types::TypeCalculator
1210
1273
  # @api private
1211
1274
  def assignable_POptionalType(t, t2)
1212
1275
  return true if t2.is_a?(Types::PUndefType)
1276
+ return true if t.optional_type.nil?
1213
1277
  if t2.is_a?(Types::POptionalType)
1214
- assignable?(t.optional_type, t2.optional_type)
1278
+ assignable?(t.optional_type, t2.optional_type || @t)
1215
1279
  else
1216
1280
  assignable?(t.optional_type, t2)
1217
1281
  end
@@ -1381,11 +1445,11 @@ class Puppet::Pops::Types::TypeCalculator
1381
1445
  # @api private
1382
1446
  def assignable_PArrayType(t, t2)
1383
1447
  if t2.is_a?(Types::PArrayType)
1384
- return false unless assignable?(t.element_type, t2.element_type)
1448
+ return false unless t.element_type.nil? || assignable?(t.element_type, t2.element_type || @t)
1385
1449
  assignable_PCollectionType(t, t2)
1386
1450
 
1387
1451
  elsif t2.is_a?(Types::PTupleType)
1388
- return false unless t2.types.all? {|t2_element| assignable?(t.element_type, t2_element) }
1452
+ return false unless t.element_type.nil? || t2.types.all? {|t2_element| assignable?(t.element_type, t2_element) }
1389
1453
  t2_regular = t2.types[0..-2]
1390
1454
  t2_ranged = t2.types[-1]
1391
1455
  t2_from, t2_to = size_range(t2.size_type)
@@ -1423,7 +1487,8 @@ class Puppet::Pops::Types::TypeCalculator
1423
1487
  case t2
1424
1488
  when Types::PHashType
1425
1489
  return true if (t.size_type.nil? || t.size_type.from == 0) && t2.is_the_empty_hash?
1426
- return false unless assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type)
1490
+ return false unless t.key_type.nil? || assignable?(t.key_type, t2.key_type || @t)
1491
+ return false unless t.element_type.nil? || assignable?(t.element_type, t2.element_type || @t)
1427
1492
  assignable_PCollectionType(t, t2)
1428
1493
  when Types::PStructType
1429
1494
  # hash must accept String as key type
@@ -1435,7 +1500,7 @@ class Puppet::Pops::Types::TypeCalculator
1435
1500
  key_type = t.key_type
1436
1501
  element_type = t.element_type
1437
1502
  ( struct_size >= min && struct_size <= max &&
1438
- t2.elements.all? {|e| instance_of(key_type, e.name) && assignable?(element_type, e.type) })
1503
+ t2.elements.all? {|e| (key_type.nil? || instance_of(key_type, e.name)) && (element_type.nil? || assignable?(element_type, e.value_type)) })
1439
1504
  else
1440
1505
  false
1441
1506
  end
@@ -1467,7 +1532,15 @@ class Puppet::Pops::Types::TypeCalculator
1467
1532
  # Data is assignable by other Data and by Array[Data] and Hash[Scalar, Data]
1468
1533
  # @api private
1469
1534
  def assignable_PDataType(t, t2)
1470
- t2.is_a?(Types::PDataType) || assignable?(@data_variant_t, t2)
1535
+ # We cannot put the NotUndefType[Data] in the @data_variant_t since that causes an endless recursion
1536
+ case t2
1537
+ when Types::PDataType
1538
+ true
1539
+ when Types::PNotUndefType
1540
+ assignable?(t, t2.type || @t)
1541
+ else
1542
+ assignable?(@data_variant_t, t2)
1543
+ end
1471
1544
  end
1472
1545
 
1473
1546
  # Assignable if t2's has the same runtime and the runtime name resolves to
@@ -1638,7 +1711,16 @@ class Puppet::Pops::Types::TypeCalculator
1638
1711
  end
1639
1712
 
1640
1713
  def string_PStructElement(t)
1641
- "'#{t.name}'=>#{string(t.type)}"
1714
+ k = t.key_type
1715
+ value_optional = assignable?(t.value_type, @nil_t)
1716
+ key_string =
1717
+ if k.is_a?(Types::POptionalType)
1718
+ # Output as literal String
1719
+ value_optional ? "'#{t.name}'" : string(k)
1720
+ else
1721
+ value_optional ? "NotUndef['#{t.name}']" : "'#{t.name}'"
1722
+ end
1723
+ "#{key_string}=>#{string(t.value_type)}"
1642
1724
  end
1643
1725
 
1644
1726
  # @api private
@@ -1704,11 +1786,30 @@ class Puppet::Pops::Types::TypeCalculator
1704
1786
  end
1705
1787
  end
1706
1788
 
1789
+ # @api private
1790
+ def string_PNotUndefType(t)
1791
+ contained_type = t.type
1792
+ if contained_type.nil? || contained_type.class == Puppet::Pops::Types::PAnyType
1793
+ 'NotUndef'
1794
+ else
1795
+ if contained_type.is_a?(Puppet::Pops::Types::PStringType) && contained_type.values.size == 1
1796
+ "NotUndef['#{contained_type.values[0]}']"
1797
+ else
1798
+ "NotUndef[#{string(contained_type)}]"
1799
+ end
1800
+ end
1801
+ end
1802
+
1707
1803
  def string_POptionalType(t)
1708
- if t.optional_type.nil?
1804
+ optional_type = t.optional_type
1805
+ if optional_type.nil?
1709
1806
  "Optional"
1710
1807
  else
1711
- "Optional[#{string(t.optional_type)}]"
1808
+ if optional_type.is_a?(Puppet::Pops::Types::PStringType) && optional_type.values.size == 1
1809
+ "Optional['#{optional_type.values[0]}']"
1810
+ else
1811
+ "Optional[#{string(optional_type)}]"
1812
+ end
1712
1813
  end
1713
1814
  end
1714
1815
 
@@ -2,9 +2,9 @@
2
2
  # @api public
3
3
  #
4
4
  module Puppet::Pops::Types::TypeFactory
5
- @type_calculator = Puppet::Pops::Types::TypeCalculator.new()
6
-
7
5
  Types = Puppet::Pops::Types
6
+ @type_calculator = Types::TypeCalculator.singleton
7
+ @undef_t = Types::PUndefType.new
8
8
 
9
9
  # Produces the Integer type
10
10
  # @api public
@@ -66,9 +66,17 @@ module Puppet::Pops::Types::TypeFactory
66
66
  end
67
67
 
68
68
  # Produces the Optional type, i.e. a short hand for Variant[T, Undef]
69
+ # If the given 'optional_type' argument is a String, then it will be
70
+ # converted into a String type that represents that string.
71
+ #
72
+ # @param optional_type [String,PAnyType,nil] the optional type
73
+ # @return [POptionalType] the created type
74
+ #
75
+ # @api public
76
+ #
69
77
  def self.optional(optional_type = nil)
70
78
  t = Types::POptionalType.new
71
- t.optional_type = type_of(optional_type)
79
+ t.optional_type = optional_type.is_a?(String) ? string(optional_type) : type_of(optional_type)
72
80
  t
73
81
  end
74
82
 
@@ -91,19 +99,48 @@ module Puppet::Pops::Types::TypeFactory
91
99
  end
92
100
 
93
101
  # Produces the Struct type, either a non parameterized instance representing
94
- # all structs (i.e. all hashes) or a hash with a given set of keys of String
95
- # type (names), bound to a value of a given type. Type may be a Ruby Class, a
96
- # Puppet Type, or an instance from which the type is inferred.
102
+ # all structs (i.e. all hashes) or a hash with entries where the key is
103
+ # either a literal String, an Enum with one entry, or a String representing exactly one value.
104
+ # The key type may also be wrapped in a NotUndef or an Optional.
97
105
  #
98
- def self.struct(name_type_hash = {})
106
+ # The value can be a ruby class, a String (interpreted as the name of a ruby class) or
107
+ # a Type.
108
+ #
109
+ # @param hash [Hash<Object, Object>] key => value hash
110
+ # @return [PStructType] the created Struct type
111
+ #
112
+ def self.struct(hash = {})
113
+ tc = @type_calculator
99
114
  t = Types::PStructType.new
100
- t.elements = name_type_hash.map do |name, type|
101
- elem = Types::PStructElement.new
102
- if name.is_a?(String) && name.empty?
103
- raise ArgumentError, "An empty String can not be used where a String[1, default] is expected"
115
+ t.elements = hash.map do |key_type, value_type|
116
+ value_type = type_of(value_type)
117
+ raise ArgumentError, 'Struct element value_type must be a Type' unless value_type.is_a?(Types::PAnyType)
118
+
119
+ # TODO: Should have stricter name rule
120
+ if key_type.is_a?(String)
121
+ raise ArgumentError, 'Struct element key cannot be an empty String' if key_type.empty?
122
+ key_type = string(key_type)
123
+ # Must make key optional if the value can be Undef
124
+ key_type = optional(key_type) if tc.assignable?(value_type, @undef_t)
125
+ else
126
+ # assert that the key type is one of String[1], NotUndef[String[1]] and Optional[String[1]]
127
+ case key_type
128
+ when Types::PNotUndefType
129
+ # We can loose the NotUndef wrapper here since String[1] isn't optional anyway
130
+ key_type = key_type.type
131
+ s = key_type
132
+ when Types::POptionalType
133
+ s = key_type.optional_type
134
+ else
135
+ s = key_type
136
+ end
137
+ unless (s.is_a?(Puppet::Pops::Types::PStringType) || s.is_a?(Puppet::Pops::Types::PEnumType)) && s.values.size == 1 && !s.values[0].empty?
138
+ raise ArgumentError, 'Unable to extract a non-empty literal string from Struct member key type' if key_type.empty?
139
+ end
104
140
  end
105
- elem.name = name
106
- elem.type = type_of(type)
141
+ elem = Types::PStructElement.new
142
+ elem.key_type = key_type
143
+ elem.value_type = value_type
107
144
  elem
108
145
  end
109
146
  t
@@ -351,6 +388,22 @@ module Puppet::Pops::Types::TypeFactory
351
388
  type
352
389
  end
353
390
 
391
+ # Produces a type for NotUndef[T]
392
+ # The given 'inst_type' can be a string in which case it will be converted into
393
+ # the type String[inst_type].
394
+ #
395
+ # @param inst_type [Type,String] the type to qualify
396
+ # @return [Puppet::Pops::Types::PNotUndefType] the NotUndef type
397
+ #
398
+ # @api public
399
+ #
400
+ def self.not_undef(inst_type = nil)
401
+ type = Types::PNotUndefType.new()
402
+ inst_type = string(inst_type) if inst_type.is_a?(String)
403
+ type.type = inst_type
404
+ type
405
+ end
406
+
354
407
  # Produces a type for Type[T]
355
408
  # @api public
356
409
  #