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
@@ -0,0 +1,102 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # @api public
5
+ class PRuntimeType < PAnyType
6
+ TYPE_NAME_OR_PATTERN = PVariantType.new([PStringType::NON_EMPTY, PTupleType.new([PRegexpType::DEFAULT, PStringType::NON_EMPTY])])
7
+
8
+ attr_reader :runtime, :name_or_pattern
9
+
10
+ # Creates a new instance of a Runtime type
11
+ #
12
+ # @param runtime [String] the name of the runtime, e.g. 'ruby'
13
+ # @param name_or_pattern [String,Array(Regexp,String)] name of runtime or two patterns, mapping Puppet name => runtime name
14
+ # @api public
15
+ def initialize(runtime, name_or_pattern)
16
+ unless runtime.nil? || runtime.is_a?(Symbol)
17
+ runtime = TypeAsserter.assert_instance_of("Runtime 'runtime'", PStringType::NON_EMPTY, runtime).to_sym
18
+ end
19
+ @runtime = runtime
20
+ @name_or_pattern = TypeAsserter.assert_instance_of("Runtime 'name_or_pattern'", TYPE_NAME_OR_PATTERN, name_or_pattern, true)
21
+ end
22
+
23
+ def hash
24
+ @runtime.hash ^ @name_or_pattern.hash
25
+ end
26
+
27
+ def eql?(o)
28
+ self.class == o.class && @runtime == o.runtime && @name_or_pattern == o.name_or_pattern
29
+ end
30
+
31
+ def instance?(o, guard = nil)
32
+ assignable?(TypeCalculator.infer(o), guard)
33
+ end
34
+
35
+ def iterable?(guard = nil)
36
+ if @runtime == :ruby && !runtime_type_name.nil?
37
+ begin
38
+ c = ClassLoader.provide(self)
39
+ return c < Iterable unless c.nil?
40
+ rescue ArgumentError
41
+ end
42
+ end
43
+ false
44
+ end
45
+
46
+ def iterable_type(guard = nil)
47
+ iterable?(guard) ? PIterableType.new(self) : nil
48
+ end
49
+
50
+ # @api private
51
+ def runtime_type_name
52
+ @name_or_pattern.is_a?(String) ? @name_or_pattern : nil
53
+ end
54
+
55
+ # @api private
56
+ def class_or_module
57
+ raise "Only ruby classes or modules can be produced by this runtime, got '#{runtime}" unless runtime == :ruby
58
+ raise 'A pattern based Runtime type cannot produce a class or module' if @name_or_pattern.is_a?(Array)
59
+ com = ClassLoader.provide(self)
60
+ raise "The name #{@name_or_pattern} does not represent a ruby class or module" if com.nil?
61
+ com
62
+ end
63
+
64
+ # @api private
65
+ def from_puppet_name(puppet_name)
66
+ if @name_or_pattern.is_a?(Array)
67
+ substituted = puppet_name.sub(*@name_or_pattern)
68
+ substituted == puppet_name ? nil : PRuntimeType.new(@runtime, substituted)
69
+ else
70
+ nil
71
+ end
72
+ end
73
+
74
+ DEFAULT = PRuntimeType.new(nil, nil)
75
+ RUBY = PRuntimeType.new(:ruby, nil)
76
+
77
+ protected
78
+
79
+ # Assignable if o's has the same runtime and the runtime name resolves to
80
+ # a class that is the same or subclass of t1's resolved runtime type name
81
+ # @api private
82
+ def _assignable?(o, guard)
83
+ return false unless o.is_a?(PRuntimeType)
84
+ return false unless @runtime == o.runtime
85
+ return true if @name_or_pattern.nil? # t1 is wider
86
+
87
+ onp = o.name_or_pattern
88
+ return true if @name_or_pattern == onp
89
+ return false unless @name_or_pattern.is_a?(String) && onp.is_a?(String)
90
+
91
+ # NOTE: This only supports Ruby, must change when/if the set of runtimes is expanded
92
+ begin
93
+ c1 = ClassLoader.provide(self)
94
+ c2 = ClassLoader.provide(o)
95
+ c1.is_a?(Module) && c2.is_a?(Module) && !!(c2 <= c1)
96
+ rescue ArgumentError
97
+ false
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,164 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # An unparameterized type that represents all VersionRange instances
5
+ #
6
+ # @api public
7
+ class PSemVerRangeType < PScalarType
8
+ # Check if a version is included in a version range. The version can be a string or
9
+ # a `Semantic::SemVer`
10
+ #
11
+ # @param range [Semantic::VersionRange] the range to match against
12
+ # @param version [Semantic::Version,String] the version to match
13
+ # @return [Boolean] `true` if the range includes the given version
14
+ #
15
+ # @api public
16
+ def self.include?(range, version)
17
+ case version
18
+ when Semantic::Version
19
+ range.include?(version)
20
+ when String
21
+ begin
22
+ range.include?(Semantic::Version.parse(version))
23
+ rescue Semantic::Version::ValidationFailure
24
+ false
25
+ end
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ # Checks if range _a_ is a sub-range of (i.e. completely covered by) range _b_
32
+ # @param a [Semantic::VersionRange] the first range
33
+ # @param b [Semantic::VersionRange] the second range
34
+ #
35
+ # @return [Boolean] `true` if _a_ is completely covered by _b_
36
+ def self.covered_by?(a, b)
37
+ b.begin <= a.begin && (b.end > a.end || b.end == a.end && (!b.exclude_end? || a.exclude_end?))
38
+ end
39
+
40
+ # Merge two ranges so that the result matches all versions matched by both. A merge
41
+ # is only possible when the ranges are either adjacent or have an overlap.
42
+ #
43
+ # @param a [Semantic::VersionRange] the first range
44
+ # @param b [Semantic::VersionRange] the second range
45
+ # @return [Semantic::VersionRange,nil] the result of the merge
46
+ #
47
+ # @api public
48
+ def self.merge(a, b)
49
+ if a.include?(b.begin) || b.include?(a.begin)
50
+ max = [a.end, b.end].max
51
+ exclude_end = false
52
+ if a.exclude_end?
53
+ exclude_end = max == a.end && (max > b.end || b.exclude_end?)
54
+ elsif b.exclude_end?
55
+ exclude_end = max == b.end && (max > a.end || a.exclude_end?)
56
+ end
57
+ Semantic::VersionRange.new([a.begin, b.begin].min, max, exclude_end)
58
+ elsif a.exclude_end? && a.end == b.begin
59
+ # Adjacent, a before b
60
+ Semantic::VersionRange.new(a.begin, b.end, b.exclude_end?)
61
+ elsif b.exclude_end? && b.end == a.begin
62
+ # Adjacent, b before a
63
+ Semantic::VersionRange.new(b.begin, a.end, a.exclude_end?)
64
+ else
65
+ # No overlap
66
+ nil
67
+ end
68
+ end
69
+
70
+ def instance?(o, guard = nil)
71
+ o.is_a?(Semantic::VersionRange)
72
+ end
73
+
74
+ def eql?(o)
75
+ self.class == o.class
76
+ end
77
+
78
+ def hash?
79
+ super ^ @version_range.hash
80
+ end
81
+
82
+ def self.new_function(_, loader)
83
+ range_expr = "\\A#{range_pattern}\\Z"
84
+ @@new_function ||= Puppet::Functions.create_loaded_function(:new_VersionRange, loader) do
85
+ local_types do
86
+ type 'SemVerRangeString = String[1]'
87
+ type 'SemVerRangeHash = Struct[{min=>Variant[default,SemVer],Optional[max]=>Variant[default,SemVer],Optional[exclude_max]=>Boolean}]'
88
+ end
89
+
90
+ # Constructs a VersionRange from a String with a format specified by
91
+ #
92
+ # https://github.com/npm/node-semver#range-grammar
93
+ #
94
+ # The logical or || operator is not implemented since it effectively builds
95
+ # an array of ranges that may be disparate. The {{Semantic::VersionRange}} inherits
96
+ # from the standard ruby range. It must be possible to describe that range in terms
97
+ # of min, max, and exclude max.
98
+ #
99
+ # The Puppet Version type is parameterized and accepts multiple ranges so creating such
100
+ # constraints is still possible. It will just require several parameters rather than one
101
+ # parameter containing the '||' operator.
102
+ #
103
+ dispatch :from_string do
104
+ param 'SemVerRangeString', :str
105
+ end
106
+
107
+ # Constructs a VersionRange from a min, and a max version. The Boolean argument denotes
108
+ # whether or not the max version is excluded or included in the range. It is included by
109
+ # default.
110
+ #
111
+ dispatch :from_versions do
112
+ param 'Variant[default,SemVer]', :min
113
+ param 'Variant[default,SemVer]', :max
114
+ optional_param 'Boolean', :exclude_max
115
+ end
116
+
117
+ # Same as #from_versions but each argument is instead given in a Hash
118
+ #
119
+ dispatch :from_hash do
120
+ param 'SemVerRangeHash', :hash_args
121
+ end
122
+
123
+ def from_string(str)
124
+ Semantic::VersionRange.parse(str)
125
+ end
126
+
127
+ def from_versions(min, max = :default, exclude_max = false)
128
+ min = Semantic::Version::MIN if min == :default
129
+ max = Semantic::Version::MAX if max == :default
130
+ Semantic::VersionRange.new(min, max, exclude_max)
131
+ end
132
+
133
+ def from_hash(hash)
134
+ from_versions(hash['min'], hash.fetch('max') { :default }, hash.fetch('exclude_max') { false })
135
+ end
136
+ end
137
+ end
138
+
139
+ DEFAULT = PSemVerRangeType.new
140
+
141
+ protected
142
+
143
+ def _assignable?(o, guard)
144
+ self == o
145
+ end
146
+
147
+ def self.range_pattern
148
+ part = '(?<part>[0-9A-Za-z-]+)'
149
+ parts = "(?<parts>#{part}(?:\\.\\g<part>)*)"
150
+
151
+ qualifier = "(?:-#{parts})?(?:\\+\\g<parts>)?"
152
+
153
+ xr = '(?<xr>[xX*]|0|[1-9][0-9]*)'
154
+ partial = "(?<partial>#{xr}(?:\\.\\g<xr>(?:\\.\\g<xr>#{qualifier})?)?)"
155
+
156
+ hyphen = "(?:#{partial}\\s+-\\s+\\g<partial>)"
157
+ simple = "(?<simple>(?:<|>|>=|<=|~|\\^)?\\g<partial>)"
158
+
159
+ "#{hyphen}|#{simple}(?:\\s+\\g<simple>)*"
160
+ end
161
+ private_class_method :range_pattern
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,113 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # A Puppet Language Type that exposes the {{Semantic::Version}} and {{Semantic::VersionRange}}.
5
+ # The version type is parameterized with version ranges.
6
+ #
7
+ # @api public
8
+ class PSemVerType < PScalarType
9
+ attr_reader :ranges
10
+
11
+ def initialize(*ranges)
12
+ ranges = ranges.map { |range| range.is_a?(Semantic::VersionRange) ? range : Semantic::VersionRange.parse(range) }
13
+ ranges = merge_ranges(ranges) if ranges.size > 1
14
+ @ranges = ranges
15
+ end
16
+
17
+ def instance?(o, guard = nil)
18
+ o.is_a?(Semantic::Version) && (@ranges.empty? || @ranges.any? {|range| range.include?(o) })
19
+ end
20
+
21
+ def eql?(o)
22
+ self.class == o.class && @ranges == o.ranges
23
+ end
24
+
25
+ def hash?
26
+ super ^ @ranges.hash
27
+ end
28
+
29
+ # @api private
30
+ def self.new_function(_, loader)
31
+ @@new_function ||= Puppet::Functions.create_loaded_function(:new_Version, loader) do
32
+ local_types do
33
+ type 'PositiveInteger = Integer[0,default]'
34
+ type 'SemVerQualifier = Pattern[/\A(?<part>[0-9A-Za-z-]+)(?:\.\g<part>)*\Z/]'
35
+ type 'SemVerString = String[1]'
36
+ type 'SemVerHash = Struct[{major=>PositiveInteger,minor=>PositiveInteger,patch=>PositiveInteger,Optional[prerelease]=>SemVerQualifier,Optional[build]=>SemVerQualifier}]'
37
+ end
38
+
39
+ # Creates a SemVer from a string as specified by http://semver.org/
40
+ #
41
+ dispatch :from_string do
42
+ param 'SemVerString', :str
43
+ end
44
+
45
+ # Creates a SemVer from integers, prerelease, and build arguments
46
+ #
47
+ dispatch :from_args do
48
+ param 'PositiveInteger', :major
49
+ param 'PositiveInteger', :minor
50
+ param 'PositiveInteger', :patch
51
+ optional_param 'SemVerQualifier', :prerelease
52
+ optional_param 'SemVerQualifier', :build
53
+ end
54
+
55
+ # Same as #from_args but each argument is instead given in a Hash
56
+ #
57
+ dispatch :from_hash do
58
+ param 'SemVerHash', :hash_args
59
+ end
60
+
61
+ def from_string(str)
62
+ Semantic::Version.parse(str)
63
+ end
64
+
65
+ def from_args(major, minor, patch, prerelease = nil, build = nil)
66
+ Semantic::Version.new(major, minor, patch, prerelease, build)
67
+ end
68
+
69
+ def from_hash(hash)
70
+ Semantic::Version.new(hash['major'], hash['minor'], hash['patch'], hash['prerelease'], hash['build'])
71
+ end
72
+ end
73
+ end
74
+
75
+ DEFAULT = PSemVerType.new
76
+
77
+ protected
78
+
79
+ def _assignable?(o, guard)
80
+ return false unless o.class == self.class
81
+ return true if @ranges.empty?
82
+ return false if o.ranges.empty?
83
+
84
+ # All ranges in o must be covered by at least one range in self
85
+ o.ranges.all? do |o_range|
86
+ @ranges.any? do |range|
87
+ PSemVerRangeType.covered_by?(o_range, range)
88
+ end
89
+ end
90
+ end
91
+
92
+ # @api private
93
+ def merge_ranges(ranges)
94
+ result = []
95
+ until ranges.empty?
96
+ unmerged = []
97
+ x = ranges.pop
98
+ result << ranges.inject(x) do |memo, y|
99
+ merged = PSemVerRangeType.merge(memo, y)
100
+ if merged.nil?
101
+ unmerged << y
102
+ else
103
+ memo = merged
104
+ end
105
+ memo
106
+ end
107
+ ranges = unmerged
108
+ end
109
+ result.reverse!
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,21 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # Marker module for implementations that are mapped to Object types
5
+ # @api public
6
+ module PuppetObject
7
+ # Returns all classes that includes this module
8
+ def self.descendants
9
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
10
+ end
11
+
12
+ # Returns the Puppet Type for this instance. The implementing class must
13
+ # add the {#_ptype} as a class method.
14
+ #
15
+ # @return [PObjectType] the type
16
+ def _ptype
17
+ self.class._ptype
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,258 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # @api private
5
+ class RubyGenerator < TypeFormatter
6
+ def remove_common_namespace(namespace_segments, name)
7
+ segments = name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)
8
+ namespace_segments.size.times do |idx|
9
+ break if segments.empty? || namespace_segments[idx] != segments[0]
10
+ segments.shift
11
+ end
12
+ segments
13
+ end
14
+
15
+ def namespace_relative(namespace_segments, name)
16
+ remove_common_namespace(namespace_segments, name).join(TypeFormatter::NAME_SEGMENT_SEPARATOR)
17
+ end
18
+
19
+ def create_class(obj)
20
+ @dynamic_classes ||= Hash.new do |hash, key|
21
+ rp = key.resolved_parent
22
+ parent_class = rp.is_a?(PObjectType) ? create_class(rp) : Object
23
+ class_def = ''
24
+ class_body(key, EMPTY_ARRAY, class_def)
25
+ cls = Class.new(parent_class)
26
+ cls.class_eval(class_def)
27
+ cls.define_singleton_method(:_ptype) { return key }
28
+ hash[key] = cls
29
+ end
30
+ @dynamic_classes[obj]
31
+ end
32
+
33
+ def module_definition(types, comment)
34
+ object_types, aliased_types = types.partition { |type| type.is_a?(PObjectType) }
35
+ impl_names = implementation_names(object_types)
36
+
37
+ # extract common implementation module prefix
38
+ common_prefix = []
39
+ segmented_names = impl_names.map { |impl_name| impl_name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR) }
40
+ segments = segmented_names[0]
41
+ segments.size.times do |idx|
42
+ segment = segments[idx]
43
+ break unless segmented_names.all? { |sn| sn[idx] == segment }
44
+ common_prefix << segment
45
+ end
46
+
47
+ # Create class definition of all contained types
48
+ bld = ''
49
+ start_module(common_prefix, comment, bld)
50
+ class_names = []
51
+ object_types.each_with_index do |type, index|
52
+ class_names << class_definition(type, common_prefix, bld, impl_names[index])
53
+ bld << "\n"
54
+ end
55
+
56
+ aliases = Hash[aliased_types.map { |type| [type.name, type.resolved_type] }]
57
+ end_module(common_prefix, aliases, class_names, bld)
58
+ bld
59
+ end
60
+
61
+ def start_module(common_prefix, comment, bld)
62
+ bld << '# ' << comment << "\n"
63
+ common_prefix.each { |cp| bld << 'module ' << cp << "\n" }
64
+ end
65
+
66
+ def end_module(common_prefix, aliases, class_names, bld)
67
+ # Emit registration of contained type aliases
68
+ unless aliases.empty?
69
+ bld << "Puppet::Pops::Pcore.register_aliases({\n"
70
+ aliases.each { |name, type| bld << " '" << name << "' => " << TypeFormatter.string(type.to_s) << "\n" }
71
+ bld.chomp!(",\n")
72
+ bld << "})\n\n"
73
+ end
74
+
75
+ # Emit registration of contained types
76
+ unless class_names.empty?
77
+ bld << "Puppet::Pops::Pcore.register_implementations(\n"
78
+ class_names.each { |class_name| bld << ' ' << class_name << ",\n" }
79
+ bld.chomp!(",\n")
80
+ bld << ")\n\n"
81
+ end
82
+ bld.chomp!("\n")
83
+
84
+ common_prefix.size.times { bld << "end\n" }
85
+ end
86
+
87
+ def implementation_names(object_types)
88
+ object_types.map do |type|
89
+ ir = Loaders.implementation_registry
90
+ impl_name = ir.module_name_for_type(type)
91
+ raise Puppet::Error, "Unable to create an instance of #{type.name}. No mapping exists to runtime object" if impl_name.nil?
92
+ impl_name[0]
93
+ end
94
+ end
95
+
96
+ def class_definition(obj, namespace_segments, bld, class_name)
97
+ module_segments = remove_common_namespace(namespace_segments, class_name)
98
+ leaf_name = module_segments.pop
99
+ module_segments.each { |segment| bld << 'module ' << segment << "\n" }
100
+ bld << 'class ' << leaf_name
101
+ segments = class_name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)
102
+
103
+ unless obj.parent.nil?
104
+ ir = Loaders.implementation_registry
105
+ parent_impl = ir.module_name_for_type(obj.parent)
106
+ raise Puppet::Error, "Unable to create an instance of #{obj.parent.name}. No mapping exists to runtime object" if parent_impl.nil?
107
+ bld << ' < ' << namespace_relative(segments, parent_impl[0])
108
+ end
109
+
110
+ bld << "\n"
111
+ bld << " def self._plocation\n"
112
+ bld << " loc = Puppet::Util.path_to_uri(\"\#{__FILE__}\")\n"
113
+ bld << " URI(\"#\{loc}?line=#\{__LINE__.to_i - 3}\")\n"
114
+ bld << " end\n"
115
+
116
+ bld << "\n"
117
+ bld << " def self._ptype\n"
118
+ bld << ' @_ptype ||= ' << namespace_relative(segments, obj.class.name) << ".new('" << obj.name << "',\n"
119
+ bld << TypeFormatter.new.ruby_string('ref', 3, obj.i12n_hash(false)) << " )\n"
120
+ bld << " end\n"
121
+
122
+ class_body(obj, segments, bld)
123
+
124
+ bld << "end\n"
125
+ module_segments.size.times { bld << "end\n" }
126
+ module_segments << leaf_name
127
+ module_segments.join(TypeFormatter::NAME_SEGMENT_SEPARATOR)
128
+ end
129
+
130
+ def class_body(obj, segments, bld)
131
+ if obj.parent.nil?
132
+ bld << "\n include " << namespace_relative(segments, Puppet::Pops::Types::PuppetObject.name) << "\n\n" # marker interface
133
+ bld << " def self.ref(type_string)\n"
134
+ bld << ' ' << namespace_relative(segments, Puppet::Pops::Types::PTypeReferenceType.name) << ".new(type_string)\n"
135
+ bld << " end\n"
136
+ end
137
+
138
+ # Output constants
139
+ constants, others = obj.attributes(true).values.partition { |a| a.kind == PObjectType::ATTRIBUTE_KIND_CONSTANT }
140
+ constants = constants.select { |ca| ca.container.equal?(obj) }
141
+ unless constants.empty?
142
+ constants.each { |ca| bld << "\n def self." << ca.name << "\n _ptype['" << ca.name << "'].value\n end\n" }
143
+ constants.each { |ca| bld << "\n def " << ca.name << "\n self.class." << ca.name << "\n end\n" }
144
+ end
145
+
146
+ init_params = others.reject { |a| a.kind == PObjectType::ATTRIBUTE_KIND_DERIVED }
147
+ opt, non_opt = init_params.partition { |ip| ip.value? }
148
+
149
+ # Output type safe hash constructor
150
+ bld << "\n def self.from_hash(i12n)\n"
151
+ bld << ' from_asserted_hash(' << namespace_relative(segments, TypeAsserter.name) << '.assert_instance_of('
152
+ bld << "'" << obj.label << " initializer', _ptype.i12n_type, i12n))\n end\n\n def self.from_asserted_hash(i12n)\n new(\n"
153
+ non_opt.each { |ip| bld << " i12n['" << ip.name << "'],\n" }
154
+ opt.each { |ip| bld << " i12n.fetch('" << ip.name << "') { _ptype['" << ip.name << "'].value },\n" }
155
+ bld.chomp!(",\n")
156
+ bld << ")\n end\n"
157
+
158
+ # Output type safe constructor
159
+ bld << "\n def self.create"
160
+ if init_params.empty?
161
+ bld << "\n new"
162
+ else
163
+ bld << '('
164
+ non_opt.each { |ip| bld << ip.name << ', ' }
165
+ opt.each { |ip| bld << ip.name << ' = ' << "_ptype['#{ip.name}'].value" << ', ' }
166
+ bld.chomp!(', ')
167
+ bld << ")\n"
168
+ bld << ' ta = ' << namespace_relative(segments, TypeAsserter.name) << "\n"
169
+ bld << " attrs = _ptype.attributes(true)\n"
170
+ init_params.each do |a|
171
+ bld << " ta.assert_instance_of('" << a.container.name << '[' << a.name << ']'
172
+ bld << "', attrs['" << a.name << "'].type, " << a.name << ")\n"
173
+ end
174
+ bld << ' new('
175
+ non_opt.each { |a| bld << a.name << ', ' }
176
+ opt.each { |a| bld << a.name << ', ' }
177
+ bld.chomp!(', ')
178
+ bld << ')'
179
+ end
180
+ bld << "\n end\n"
181
+
182
+ # Output initializer
183
+ bld << "\n def initialize"
184
+ unless init_params.empty?
185
+ bld << '('
186
+ non_opt.each { |ip| bld << ip.name << ', ' }
187
+ opt.each { |ip| bld << ip.name << ' = ' << "_ptype['#{ip.name}'].value" << ', ' }
188
+ bld.chomp!(', ')
189
+ bld << ')'
190
+ unless obj.parent.nil?
191
+ bld << "\n super"
192
+ super_args = (non_opt + opt).select { |ip| !ip.container.equal?(obj) }
193
+ unless super_args.empty?
194
+ bld << '('
195
+ super_args.each { |ip| bld << ip.name << ', ' }
196
+ bld.chomp!(', ')
197
+ bld << ')'
198
+ end
199
+ end
200
+ end
201
+ bld << "\n"
202
+
203
+ init_params.each { |a| bld << ' @' << a.name << ' = ' << a.name << "\n" if a.container.equal?(obj) }
204
+ bld << " end\n\n"
205
+
206
+ # Output attr_readers
207
+ others.each do |a|
208
+ next unless a.container.equal?(obj)
209
+ if a.kind == PObjectType::ATTRIBUTE_KIND_DERIVED || a.kind == PObjectType::ATTRIBUTE_KIND_GIVEN_OR_DERIVED
210
+ bld << ' def ' << a.name << "\n"
211
+ bld << " raise Puppet::Error, \"no method is implemented for derived attribute #{a.label}\"\n"
212
+ bld << " end\n"
213
+ else
214
+ bld << ' attr_reader :' << a.name << "\n"
215
+ end
216
+ end
217
+
218
+ # Output function placeholders
219
+ obj.functions(false).each_value do |func|
220
+ bld << "\n def " << func.name << "(*args)\n"
221
+ bld << " # Placeholder for #{func.type}\n"
222
+ bld << " raise Puppet::Error, \"no method is implemented for #{func.label}\"\n"
223
+ bld << " end\n"
224
+ end
225
+
226
+ # output hash and equality
227
+ include_class = obj.include_class_in_equality?
228
+ if obj.equality.nil?
229
+ eq_names = obj.attributes(false).values.select { |a| a.kind != PObjectType::ATTRIBUTE_KIND_CONSTANT }.map(&:name)
230
+ else
231
+ eq_names = obj.equality
232
+ end
233
+
234
+ unless eq_names.empty? && !include_class
235
+ bld << "\n def hash\n "
236
+ bld << 'super.hash ^ ' unless obj.parent.nil?
237
+ if eq_names.empty?
238
+ bld << "self.class.hash\n"
239
+ else
240
+ bld << '['
241
+ bld << 'self.class, ' if include_class
242
+ eq_names.each { |eqn| bld << '@' << eqn << ', ' }
243
+ bld.chomp!(', ')
244
+ bld << "].hash\n"
245
+ end
246
+ bld << " end\n"
247
+
248
+ bld << "\n def eql?(o)\n"
249
+ bld << " super.eql?(o) &&\n" unless obj.parent.nil?
250
+ bld << " self.class.eql?(o.class) &&\n" if include_class
251
+ eq_names.each { |eqn| bld << ' @' << eqn << '.eql?(o.' << eqn << ") &&\n" }
252
+ bld.chomp!(" &&\n")
253
+ bld << "\n end\n"
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end