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
@@ -126,7 +126,7 @@ module Validation
126
126
  #
127
127
  def []=(issue, level)
128
128
  raise Puppet::DevError.new("Attempt to set validation severity for something that is not an Issue. (Got #{issue.class})") unless issue.is_a? Issues::Issue
129
- raise Puppet::DevError.new("Illegal severity level: #{option}") unless @@severity_hash[level]
129
+ raise Puppet::DevError.new("Illegal severity level: #{level} for '#{issue.issue_code}'") unless @@severity_hash[level]
130
130
  raise Puppet::DevError.new("Attempt to demote the hard issue '#{issue.issue_code}' to #{level}") unless issue.demotable? || level == :error
131
131
  @severities[issue] = level
132
132
  end
@@ -11,7 +11,7 @@ module Validation
11
11
  # Model objects via their metamodel. (I.e an extra call to multiplicity check in polymorph check).
12
12
  # This is however mostly valuable when validating model to model transformations, and is therefore T.B.D
13
13
  #
14
- class Checker4_0
14
+ class Checker4_0 < Evaluator::LiteralEvaluator
15
15
  attr_reader :acceptor
16
16
  attr_reader :migration_checker
17
17
 
@@ -19,6 +19,7 @@ class Checker4_0
19
19
  # `:will_accept?` and `:accept`.
20
20
  #
21
21
  def initialize(diagnostics_producer)
22
+ super()
22
23
  @@check_visitor ||= Visitor.new(nil, "check", 0, 0)
23
24
  @@rvalue_visitor ||= Visitor.new(nil, "rvalue", 0, 0)
24
25
  @@hostname_visitor ||= Visitor.new(nil, "hostname", 1, 2)
@@ -235,7 +236,13 @@ class Checker4_0
235
236
  end
236
237
 
237
238
  def check_CallNamedFunctionExpression(o)
238
- case o.functor_expr
239
+ functor = o.functor_expr
240
+ if functor.is_a?(Model::QualifiedReference) ||
241
+ functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference)
242
+ # ok (a call to a type)
243
+ return nil
244
+ end
245
+ case functor
239
246
  when Model::QualifiedName
240
247
  # ok
241
248
  nil
@@ -355,6 +362,50 @@ class Checker4_0
355
362
  internal_check_type_ref(o, o.type_expr)
356
363
  end
357
364
 
365
+ def check_TypeMapping(o)
366
+ top(o.eContainer, o)
367
+ lhs = o.type_expr
368
+ lhs_type = 0 # Not Runtime
369
+ if lhs.is_a?(Model::AccessExpression)
370
+ left = lhs.left_expr
371
+ if left.is_a?(Model::QualifiedReference) && left.cased_value == 'Runtime'
372
+ lhs_type = 1 # Runtime
373
+ keys = lhs.keys
374
+
375
+ # Must be a literal string or pattern replacement
376
+ lhs_type = 2 if keys.size == 2 && pattern_with_replacement?(keys[1])
377
+ end
378
+ end
379
+
380
+ if lhs_type == 0
381
+ # This is not a TypeMapping. Something other than Runtime[] on LHS
382
+ acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o)
383
+ else
384
+ rhs = o.mapping_expr
385
+ if pattern_with_replacement?(rhs)
386
+ acceptor.accept(Issues::ILLEGAL_SINGLE_TYPE_MAPPING, o) if lhs_type == 1
387
+ elsif type_ref?(rhs)
388
+ acceptor.accept(Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o) if lhs_type == 2
389
+ else
390
+ acceptor.accept(lhs_type == 1 ? Issues::ILLEGAL_SINGLE_TYPE_MAPPING : Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o)
391
+ end
392
+ end
393
+ end
394
+
395
+ def pattern_with_replacement?(o)
396
+ if o.is_a?(Model::LiteralList)
397
+ v = o.values
398
+ v.size == 2 && v[0].is_a?(Model::LiteralRegularExpression) && v[1].is_a?(Model::LiteralString)
399
+ else
400
+ false
401
+ end
402
+ end
403
+
404
+ def type_ref?(o)
405
+ o = o.left_expr if o.is_a?(Model::AccessExpression)
406
+ o.is_a?(Model::QualifiedReference)
407
+ end
408
+
358
409
  def check_TypeDefinition(o)
359
410
  top(o.eContainer, o)
360
411
  internal_check_reserved_type_name(o, o.name)
@@ -467,6 +518,17 @@ class Checker4_0
467
518
  o.values.each {|v| rvalue(v) }
468
519
  end
469
520
 
521
+ def check_LiteralHash(o)
522
+ # the keys of a literal hash may be non-literal expressions. They cannot be checked.
523
+ unique = Set.new
524
+ o.entries.each do |entry|
525
+ catch(:not_literal) do
526
+ literal_key = literal(entry.key)
527
+ acceptor.accept(Issues::DUPLICATE_KEY, entry, {:key => literal_key}) if unique.add?(literal_key).nil?
528
+ end
529
+ end
530
+ end
531
+
470
532
  def check_NodeDefinition(o)
471
533
  # Check that hostnames are valid hostnames (or regular expressions)
472
534
  hostname(o.host_matches, o)
@@ -491,8 +553,8 @@ class Checker4_0
491
553
  # DOH: QualifiedReferences are created with LOWER CASE NAMES at parse time
492
554
  def check_QualifiedReference(o)
493
555
  # Is this a valid qualified name?
494
- if o.value !~ Patterns::CLASSREF
495
- acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.value})
556
+ if o.cased_value !~ Patterns::CLASSREF_EXT
557
+ acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.cased_value})
496
558
  end
497
559
  end
498
560
 
@@ -27,6 +27,7 @@ class ValidatorFactory_4_0 < Factory
27
27
 
28
28
  p[Issues::FUTURE_RESERVED_WORD] = :deprecation
29
29
 
30
+ p[Issues::DUPLICATE_KEY] = Puppet[:strict] == :off ? :ignore : Puppet[:strict]
30
31
  p[Issues::NAME_WITH_HYPHEN] = :error
31
32
  p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :ignore
32
33
  p
@@ -40,7 +40,9 @@ class Puppet::Pops::Visitor
40
40
  return receiver.send(method_name, thing, *args)
41
41
  else
42
42
  thing.class.ancestors().each do |ancestor|
43
- method_name = :"#{@message}_#{ancestor.name.split(DOUBLE_COLON).last}"
43
+ name = ancestor.name
44
+ next if name.nil?
45
+ method_name = :"#{@message}_#{name.split(DOUBLE_COLON).last}"
44
46
  next unless receiver.respond_to?(method_name, true)
45
47
  @cache[thing.class] = method_name
46
48
  return receiver.send(method_name, thing, *args)
@@ -521,7 +521,7 @@ class Puppet::Property < Puppet::Parameter
521
521
  if features = self.class.value_option(self.class.value_name(value), :required_features)
522
522
  features = Array(features)
523
523
  needed_features = features.collect { |f| f.to_s }.join(", ")
524
- raise ArgumentError, "Provider must have features '#{needed_features}' to set '#{self.class.name}' to '#{value}'" unless provider.satisfies?(features)
524
+ raise ArgumentError, "Provider #{provider.class.name} must have features '#{needed_features}' to set '#{self.class.name}' to '#{value}'" unless provider.satisfies?(features)
525
525
  end
526
526
  end
527
527
 
@@ -425,7 +425,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
425
425
  end
426
426
 
427
427
  unless force
428
- # If we have a verison of augeas which is at least 0.3.6 then we
428
+ # If we have a version of augeas which is at least 0.3.6 then we
429
429
  # can make the changes now and see if changes were made.
430
430
  if return_value and versioncmp(get_augeas_version, "0.3.6") >= 0
431
431
  debug("Will attempt to save and only run if files changed")
@@ -30,7 +30,7 @@ Puppet::Type.type(:package).provide :pip,
30
30
  # that's managed by `pip` or an empty array if `pip` is not available.
31
31
  def self.instances
32
32
  packages = []
33
- pip_cmd = cmd.map { |c| which(c) }.find { |c| c != nil }
33
+ pip_cmd = self.pip_cmd
34
34
  return [] unless pip_cmd
35
35
  execpipe "#{pip_cmd} freeze" do |process|
36
36
  process.collect do |line|
@@ -38,6 +38,13 @@ Puppet::Type.type(:package).provide :pip,
38
38
  packages << new(options)
39
39
  end
40
40
  end
41
+
42
+ # Pip can also upgrade pip, but it's not listed in freeze so need to special case it
43
+ # Pip list would also show pip installed version, but "pip list" doesn't exist for older versions of pip (E.G v1.0)
44
+ if version = self.pip_version
45
+ packages << new({:ensure => version, :name => File.basename(pip_cmd), :provider => name})
46
+ end
47
+
41
48
  packages
42
49
  end
43
50
 
@@ -45,6 +52,21 @@ Puppet::Type.type(:package).provide :pip,
45
52
  ["pip", "pip-python"]
46
53
  end
47
54
 
55
+ def self.pip_cmd
56
+ self.cmd.map { |c| which(c) }.find { |c| c != nil }
57
+ end
58
+
59
+ def self.pip_version
60
+ pip_cmd = self.pip_cmd
61
+ return nil unless pip_cmd
62
+
63
+ execpipe [pip_cmd, '--version'] do |process|
64
+ process.collect do |line|
65
+ return line.strip.match(/^pip (\d+\.\d+\.?\d*).*$/)[1]
66
+ end
67
+ end
68
+ end
69
+
48
70
  # Return structured information about a particular package or `nil` if
49
71
  # it is not installed or `pip` itself is not available.
50
72
  def query
@@ -54,26 +76,13 @@ Puppet::Type.type(:package).provide :pip,
54
76
  return nil
55
77
  end
56
78
 
57
- # Ask the PyPI API for the latest version number. There is no local
58
- # cache of PyPI's package list so this operation will always have to
59
- # ask the web service.
79
+ # Use pip CLI to look up versions from PyPI repositories, honoring local pip config such as custom repositories
60
80
  def latest
61
- http_proxy_host = Puppet::Util::HttpProxy.http_proxy_host
62
- http_proxy_port = Puppet::Util::HttpProxy.http_proxy_port
63
- if http_proxy_host && http_proxy_port
64
- proxy = "#{http_proxy_host}:#{http_proxy_port}"
65
- else
66
- # nil is acceptable
67
- proxy = http_proxy_host
81
+ return nil unless self.class.pip_cmd
82
+ if Puppet::Util::Package.versioncmp(self.class.pip_version, '1.5.4') == -1 # a < b
83
+ return latest_with_old_pip
68
84
  end
69
-
70
- client = XMLRPC::Client.new2("http://pypi.python.org/pypi", proxy)
71
- client.http_header_extra = {"Content-Type" => "text/xml"}
72
- client.timeout = 10
73
- result = client.call("package_releases", @resource[:name])
74
- result.first
75
- rescue Timeout::Error => detail
76
- raise Puppet::Error, "Timeout while contacting pypi.python.org: #{detail}", detail.backtrace
85
+ latest_with_new_pip
77
86
  end
78
87
 
79
88
  # Install a package. The ensure parameter may specify installed,
@@ -120,8 +129,14 @@ Puppet::Type.type(:package).provide :pip,
120
129
  def lazy_pip(*args)
121
130
  pip *args
122
131
  rescue NoMethodError => e
132
+ # Ensure pip can upgrade pip, which usually puts pip into a new path /usr/local/bin/pip (compared to /usr/bin/pip)
133
+ # The path to pip needs to be looked up again in the subsequent request. Using the preferred approach as noted
134
+ # in provider.rb ensures this (copied below for reference)
135
+ #
136
+ # @note From provider.rb; It is preferred if the commands are not entered with absolute paths as this allows puppet
137
+ # to search for them using the PATH variable.
123
138
  if pathname = self.class.cmd.map { |c| which(c) }.find { |c| c != nil }
124
- self.class.commands :pip => pathname
139
+ self.class.commands :pip => File.basename(pathname)
125
140
  pip *args
126
141
  else
127
142
  raise e, "Could not locate command #{self.class.cmd.join(' and ')}.", e.backtrace
@@ -131,4 +146,33 @@ Puppet::Type.type(:package).provide :pip,
131
146
  def install_options
132
147
  join_options(@resource[:install_options])
133
148
  end
149
+
150
+ def latest_with_new_pip
151
+ # Less resource intensive approach for pip version 1.5.4 and above
152
+ execpipe ["#{self.class.pip_cmd}", "install", "#{@resource[:name]}==versionplease"] do |process|
153
+ process.collect do |line|
154
+ # PIP OUTPUT: Could not find a version that satisfies the requirement Django==versionplease (from versions: 1.1.3, 1.8rc1)
155
+ if line =~ /from versions: /
156
+ textAfterLastMatch = $'
157
+ versionList = textAfterLastMatch.chomp(")\n").split(', ')
158
+ return versionList.last
159
+ end
160
+ end
161
+ return nil
162
+ end
163
+ end
164
+
165
+ def latest_with_old_pip
166
+ Dir.mktmpdir("puppet_pip") do |dir|
167
+ execpipe ["#{self.class.pip_cmd}", "install", "#{@resource[:name]}", "-d", "#{dir}", "-v"] do |process|
168
+ process.collect do |line|
169
+ # PIP OUTPUT: Using version 0.10.1 (newest of versions: 0.10.1, 0.10, 0.9, 0.8.1, 0.8, 0.7.2, 0.7.1, 0.7, 0.6.1, 0.6, 0.5.2, 0.5.1, 0.5, 0.4, 0.3.1, 0.3, 0.2, 0.1)
170
+ if line =~ /Using version (.+?) \(newest of versions/
171
+ return $1
172
+ end
173
+ end
174
+ return nil
175
+ end
176
+ end
177
+ end
134
178
  end
@@ -22,6 +22,31 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
22
22
  self::NEVRA_REGEX = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)$}
23
23
  self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]
24
24
 
25
+ ARCH_LIST = [
26
+ 'noarch',
27
+ 'i386',
28
+ 'i686',
29
+ 'ppc',
30
+ 'ppc64',
31
+ 'armv3l',
32
+ 'armv4b',
33
+ 'armv4l',
34
+ 'armv4tl',
35
+ 'armv5tel',
36
+ 'armv5tejl',
37
+ 'armv6l',
38
+ 'armv7l',
39
+ 'm68kmint',
40
+ 's390',
41
+ 's390x',
42
+ 'ia64',
43
+ 'x86_64',
44
+ 'sh3',
45
+ 'sh4',
46
+ ]
47
+
48
+ ARCH_REGEX = Regexp.new(ARCH_LIST.join('|\.'))
49
+
25
50
  commands :rpm => "rpm"
26
51
 
27
52
  if command('rpm')
@@ -268,6 +293,92 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
268
293
  end
269
294
  end
270
295
 
296
+ def insync?(is)
297
+ return false if [:purged, :absent].include?(is)
298
+ should = resource[:ensure]
299
+ 0 == rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(is))
300
+ end
301
+
302
+ # parse a rpm "version" specification
303
+ # this re-implements rpm's
304
+ # rpmUtils.miscutils.stringToVersion() in ruby
305
+ def rpm_parse_evr(s)
306
+ ei = s.index(':')
307
+ if ei
308
+ e = s[0,ei]
309
+ s = s[ei+1,s.length]
310
+ else
311
+ e = nil
312
+ end
313
+ begin
314
+ e = String(Integer(e))
315
+ rescue
316
+ # If there are non-digits in the epoch field, default to nil
317
+ e = nil
318
+ end
319
+ ri = s.index('-')
320
+ if ri
321
+ v = s[0,ri]
322
+ r = s[ri+1,s.length]
323
+ if arch = r.scan(ARCH_REGEX)[0]
324
+ a = arch.gsub(/\./, '')
325
+ r.gsub!(ARCH_REGEX, '')
326
+ end
327
+ else
328
+ v = s
329
+ r = nil
330
+ end
331
+ return { :epoch => e, :version => v, :release => r, :arch => a }
332
+ end
333
+
334
+ # how rpm compares two package versions:
335
+ # rpmUtils.miscutils.compareEVR(), which massages data types and then calls
336
+ # rpm.labelCompare(), found in rpm.git/python/header-py.c, which
337
+ # sets epoch to 0 if null, then compares epoch, then ver, then rel
338
+ # using compare_values() and returns the first non-0 result, else 0.
339
+ # This function combines the logic of compareEVR() and labelCompare().
340
+ #
341
+ # "version_should" can be v, v-r, or e:v-r.
342
+ # "version_is" will always be at least v-r, can be e:v-r
343
+ def rpm_compareEVR(should_hash, is_hash)
344
+ # pass on to rpm labelCompare
345
+
346
+ if !should_hash[:epoch].nil?
347
+ rc = compare_values(should_hash[:epoch], is_hash[:epoch])
348
+ return rc unless rc == 0
349
+ end
350
+
351
+ rc = compare_values(should_hash[:version], is_hash[:version])
352
+ return rc unless rc == 0
353
+
354
+ # here is our special case, PUP-1244.
355
+ # if should_hash[:release] is nil (not specified by the user),
356
+ # and comparisons up to here are equal, return equal. We need to
357
+ # evaluate to whatever level of detail the user specified, so we
358
+ # don't end up upgrading or *downgrading* when not intended.
359
+ #
360
+ # This should NOT be triggered if we're trying to ensure latest.
361
+ return 0 if should_hash[:release].nil?
362
+
363
+ rc = compare_values(should_hash[:release], is_hash[:release])
364
+
365
+ return rc
366
+ end
367
+
368
+ # this method is a native implementation of the
369
+ # compare_values function in rpm's python bindings,
370
+ # found in python/header-py.c, as used by rpm.
371
+ def compare_values(s1, s2)
372
+ if s1.nil? && s2.nil?
373
+ return 0
374
+ elsif ( not s1.nil? ) && s2.nil?
375
+ return 1
376
+ elsif s1.nil? && (not s2.nil?)
377
+ return -1
378
+ end
379
+ return rpmvercmp(s1, s2)
380
+ end
381
+
271
382
  private
272
383
  # @param line [String] one line of rpm package query information
273
384
  # @return [Hash] of NEVRA_FIELDS strings parsed from package info
@@ -281,6 +392,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
281
392
  self::NEVRA_FIELDS.zip(match.captures) { |f, v| hash[f] = v }
282
393
  hash[:provider] = self.name
283
394
  hash[:ensure] = "#{hash[:version]}-#{hash[:release]}"
395
+ hash[:ensure].prepend("#{hash[:epoch]}:") if hash[:epoch] != '0'
284
396
  else
285
397
  Puppet.debug("Failed to match rpm line #{line}")
286
398
  end
@@ -149,8 +149,13 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
149
149
  else
150
150
  # Add the package version
151
151
  wanted += "-#{should}"
152
+ if wanted.scan(ARCH_REGEX)
153
+ self.debug "Detected Arch argument in package! - Moving arch to end of version string"
154
+ wanted.gsub!(/(.+)(#{ARCH_REGEX})(.+)/,'\1\3\2')
155
+ end
156
+
152
157
  is = self.query
153
- if is && yum_compareEVR(yum_parse_evr(should), yum_parse_evr(is[:ensure])) < 0
158
+ if is && rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(is[:ensure])) < 0
154
159
  self.debug "Downgrading package #{@resource[:name]} from version #{is[:ensure]} to #{should}"
155
160
  operation = :downgrade
156
161
  end
@@ -173,7 +178,7 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
173
178
 
174
179
  # FIXME: Should we raise an exception even if should == :latest
175
180
  # and yum updated us to a version other than @param_hash[:ensure] ?
176
- vercmp_result = yum_compareEVR(yum_parse_evr(should), yum_parse_evr(is[:ensure]))
181
+ vercmp_result = rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(is[:ensure]))
177
182
  raise Puppet::Error, "Failed to update to version #{should}, got version #{is[:ensure]} instead" if vercmp_result != 0
178
183
  end
179
184
  end
@@ -202,72 +207,6 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
202
207
  execute([command(:cmd), "-y", :erase, @resource[:name]])
203
208
  end
204
209
 
205
- # parse a yum "version" specification
206
- # this re-implements yum's
207
- # rpmUtils.miscutils.stringToVersion() in ruby
208
- def yum_parse_evr(s)
209
- ei = s.index(':')
210
- if ei
211
- e = s[0,ei]
212
- s = s[ei+1,s.length]
213
- else
214
- e = nil
215
- end
216
- e = String(Bignum(e)) rescue '0'
217
- ri = s.index('-')
218
- if ri
219
- v = s[0,ri]
220
- r = s[ri+1,s.length]
221
- else
222
- v = s
223
- r = nil
224
- end
225
- return { :epoch => e, :version => v, :release => r }
226
- end
227
-
228
- # how yum compares two package versions:
229
- # rpmUtils.miscutils.compareEVR(), which massages data types and then calls
230
- # rpm.labelCompare(), found in rpm.git/python/header-py.c, which
231
- # sets epoch to 0 if null, then compares epoch, then ver, then rel
232
- # using compare_values() and returns the first non-0 result, else 0.
233
- # This function combines the logic of compareEVR() and labelCompare().
234
- #
235
- # "version_should" can be v, v-r, or e:v-r.
236
- # "version_is" will always be at least v-r, can be e:v-r
237
- def yum_compareEVR(should_hash, is_hash)
238
- # pass on to rpm labelCompare
239
- rc = compare_values(should_hash[:epoch], is_hash[:epoch])
240
- return rc unless rc == 0
241
- rc = compare_values(should_hash[:version], is_hash[:version])
242
- return rc unless rc == 0
243
-
244
- # here is our special case, PUP-1244.
245
- # if should_hash[:release] is nil (not specified by the user),
246
- # and comparisons up to here are equal, return equal. We need to
247
- # evaluate to whatever level of detail the user specified, so we
248
- # don't end up upgrading or *downgrading* when not intended.
249
- #
250
- # This should NOT be triggered if we're trying to ensure latest.
251
- return 0 if should_hash[:release].nil?
252
-
253
- rc = compare_values(should_hash[:release], is_hash[:release])
254
- return rc
255
- end
256
-
257
- # this method is a native implementation of the
258
- # compare_values function in rpm's python bindings,
259
- # found in python/header-py.c, as used by yum.
260
- def compare_values(s1, s2)
261
- if s1.nil? && s2.nil?
262
- return 0
263
- elsif ( not s1.nil? ) && s2.nil?
264
- return 1
265
- elsif s1.nil? && (not s2.nil?)
266
- return -1
267
- end
268
- return rpmvercmp(s1, s2)
269
- end
270
-
271
210
  private
272
211
 
273
212
  def enablerepo