puppet 6.14.0 → 6.15.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 (195) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +15 -15
  3. data/ext/windows/service/daemon.rb +3 -3
  4. data/lib/puppet.rb +1 -1
  5. data/lib/puppet/agent.rb +2 -10
  6. data/lib/puppet/application/agent.rb +2 -1
  7. data/lib/puppet/application/filebucket.rb +5 -14
  8. data/lib/puppet/application/ssl.rb +2 -2
  9. data/lib/puppet/configurer.rb +7 -3
  10. data/lib/puppet/configurer/plugin_handler.rb +1 -1
  11. data/lib/puppet/defaults.rb +22 -2
  12. data/lib/puppet/environments.rb +4 -5
  13. data/lib/puppet/face/plugin.rb +1 -1
  14. data/lib/puppet/file_system/file_impl.rb +13 -9
  15. data/lib/puppet/forge/repository.rb +1 -1
  16. data/lib/puppet/functions/call.rb +1 -1
  17. data/lib/puppet/functions/reduce.rb +2 -4
  18. data/lib/puppet/http.rb +2 -0
  19. data/lib/puppet/http/client.rb +191 -52
  20. data/lib/puppet/http/external_client.rb +96 -0
  21. data/lib/puppet/http/redirector.rb +34 -0
  22. data/lib/puppet/http/resolver.rb +46 -3
  23. data/lib/puppet/http/resolver/server_list.rb +75 -15
  24. data/lib/puppet/http/resolver/settings.rb +22 -2
  25. data/lib/puppet/http/resolver/srv.rb +28 -2
  26. data/lib/puppet/http/response.rb +63 -1
  27. data/lib/puppet/http/retry_after_handler.rb +39 -0
  28. data/lib/puppet/http/service.rb +67 -1
  29. data/lib/puppet/http/service/ca.rb +71 -9
  30. data/lib/puppet/http/service/compiler.rb +213 -11
  31. data/lib/puppet/http/service/file_server.rb +105 -4
  32. data/lib/puppet/http/service/report.rb +36 -3
  33. data/lib/puppet/http/session.rb +59 -8
  34. data/lib/puppet/indirector/catalog/rest.rb +2 -1
  35. data/lib/puppet/indirector/facts/rest.rb +2 -1
  36. data/lib/puppet/indirector/file_bucket_file/rest.rb +48 -0
  37. data/lib/puppet/indirector/file_metadata/rest.rb +4 -2
  38. data/lib/puppet/indirector/node/rest.rb +2 -1
  39. data/lib/puppet/indirector/report/yaml.rb +23 -0
  40. data/lib/puppet/indirector/status/rest.rb +2 -1
  41. data/lib/puppet/metatype/manager.rb +80 -80
  42. data/lib/puppet/network/http/base_pool.rb +6 -1
  43. data/lib/puppet/network/http/pool.rb +2 -4
  44. data/lib/puppet/network/http_pool.rb +1 -0
  45. data/lib/puppet/node/environment.rb +11 -1
  46. data/lib/puppet/pal/pal_impl.rb +1 -29
  47. data/lib/puppet/parser/compiler.rb +14 -7
  48. data/lib/puppet/parser/functions.rb +18 -13
  49. data/lib/puppet/pops/loaders.rb +7 -5
  50. data/lib/puppet/provider/group/windows_adsi.rb +3 -3
  51. data/lib/puppet/provider/package/apt.rb +61 -1
  52. data/lib/puppet/provider/package/dnfmodule.rb +39 -12
  53. data/lib/puppet/provider/package/gem.rb +41 -7
  54. data/lib/puppet/provider/package/pacman.rb +2 -5
  55. data/lib/puppet/provider/package/pip.rb +105 -33
  56. data/lib/puppet/provider/package/pip3.rb +0 -2
  57. data/lib/puppet/provider/package/pkgdmg.rb +1 -1
  58. data/lib/puppet/provider/package/pkgng.rb +16 -4
  59. data/lib/puppet/provider/package/puppet_gem.rb +6 -2
  60. data/lib/puppet/provider/package/rpm.rb +6 -213
  61. data/lib/puppet/provider/package/yum.rb +92 -19
  62. data/lib/puppet/provider/service/systemd.rb +2 -1
  63. data/lib/puppet/reports/http.rb +13 -11
  64. data/lib/puppet/resource/type_collection.rb +20 -16
  65. data/lib/puppet/ssl.rb +1 -0
  66. data/lib/puppet/ssl/host.rb +4 -4
  67. data/lib/puppet/ssl/oids.rb +1 -0
  68. data/lib/puppet/ssl/state_machine.rb +50 -33
  69. data/lib/puppet/transaction/report.rb +2 -2
  70. data/lib/puppet/type.rb +6 -1
  71. data/lib/puppet/type/file/source.rb +4 -2
  72. data/lib/puppet/type/package.rb +25 -2
  73. data/lib/puppet/type/user.rb +0 -19
  74. data/lib/puppet/util/at_fork.rb +1 -1
  75. data/lib/puppet/util/autoload.rb +3 -0
  76. data/lib/puppet/util/instance_loader.rb +14 -10
  77. data/lib/puppet/util/package/version/debian.rb +175 -0
  78. data/lib/puppet/util/package/version/gem.rb +15 -0
  79. data/lib/puppet/util/package/version/pip.rb +167 -0
  80. data/lib/puppet/util/package/version/range.rb +50 -0
  81. data/lib/puppet/util/package/version/range/gt.rb +14 -0
  82. data/lib/puppet/util/package/version/range/gt_eq.rb +14 -0
  83. data/lib/puppet/util/package/version/range/lt.rb +14 -0
  84. data/lib/puppet/util/package/version/range/lt_eq.rb +14 -0
  85. data/lib/puppet/util/package/version/range/min_max.rb +21 -0
  86. data/lib/puppet/util/package/version/range/simple.rb +11 -0
  87. data/lib/puppet/util/package/version/rpm.rb +73 -0
  88. data/lib/puppet/util/pidlock.rb +13 -7
  89. data/lib/puppet/util/platform.rb +5 -0
  90. data/lib/puppet/util/rpm_compare.rb +193 -0
  91. data/lib/puppet/util/windows/adsi.rb +2 -2
  92. data/lib/puppet/util/windows/process.rb +15 -14
  93. data/lib/puppet/util/windows/security.rb +1 -0
  94. data/lib/puppet/util/windows/sid.rb +3 -3
  95. data/lib/puppet/version.rb +1 -1
  96. data/locales/puppet.pot +207 -201
  97. data/man/man5/puppet.conf.5 +11 -3
  98. data/man/man8/puppet-agent.8 +1 -1
  99. data/man/man8/puppet-apply.8 +1 -1
  100. data/man/man8/puppet-catalog.8 +1 -1
  101. data/man/man8/puppet-config.8 +1 -1
  102. data/man/man8/puppet-describe.8 +1 -1
  103. data/man/man8/puppet-device.8 +1 -1
  104. data/man/man8/puppet-doc.8 +1 -1
  105. data/man/man8/puppet-epp.8 +1 -1
  106. data/man/man8/puppet-facts.8 +1 -1
  107. data/man/man8/puppet-filebucket.8 +1 -1
  108. data/man/man8/puppet-generate.8 +1 -1
  109. data/man/man8/puppet-help.8 +1 -1
  110. data/man/man8/puppet-key.8 +1 -1
  111. data/man/man8/puppet-lookup.8 +1 -1
  112. data/man/man8/puppet-man.8 +1 -1
  113. data/man/man8/puppet-module.8 +1 -1
  114. data/man/man8/puppet-node.8 +1 -1
  115. data/man/man8/puppet-parser.8 +1 -1
  116. data/man/man8/puppet-plugin.8 +1 -1
  117. data/man/man8/puppet-report.8 +1 -1
  118. data/man/man8/puppet-resource.8 +1 -1
  119. data/man/man8/puppet-script.8 +1 -1
  120. data/man/man8/puppet-ssl.8 +1 -1
  121. data/man/man8/puppet-status.8 +1 -1
  122. data/man/man8/puppet.8 +2 -2
  123. data/spec/fixtures/ssl/unknown-127.0.0.1-key.pem +67 -0
  124. data/spec/fixtures/ssl/unknown-127.0.0.1.pem +48 -0
  125. data/spec/fixtures/ssl/unknown-ca-key.pem +67 -0
  126. data/spec/fixtures/ssl/unknown-ca.pem +59 -0
  127. data/spec/fixtures/unit/provider/package/dnfmodule/{dnf-module-list-installed.txt → dnf-module-list-enabled.txt} +2 -0
  128. data/spec/fixtures/unit/provider/package/pkgng/pkg.version +2 -0
  129. data/spec/fixtures/unit/provider/package/yum/yum-check-update-subscription-manager.txt +9 -0
  130. data/spec/fixtures/unit/provider/service/systemd/list_unit_files_services +9 -0
  131. data/spec/integration/application/agent_spec.rb +329 -0
  132. data/spec/integration/application/apply_spec.rb +132 -3
  133. data/spec/integration/application/filebucket_spec.rb +190 -0
  134. data/spec/integration/application/plugin_spec.rb +50 -0
  135. data/spec/integration/http/client_spec.rb +34 -40
  136. data/spec/integration/indirector/report/yaml.rb +83 -0
  137. data/spec/integration/module_tool/forge_spec.rb +2 -15
  138. data/spec/integration/network/http_pool_spec.rb +11 -19
  139. data/spec/integration/node/environment_spec.rb +15 -0
  140. data/spec/integration/util/windows/adsi_spec.rb +1 -1
  141. data/spec/lib/puppet/test_ca.rb +2 -2
  142. data/spec/lib/puppet_spec/https.rb +10 -7
  143. data/spec/lib/puppet_spec/puppetserver.rb +119 -0
  144. data/spec/shared_contexts/https.rb +29 -0
  145. data/spec/unit/agent_spec.rb +33 -25
  146. data/spec/unit/application/agent_spec.rb +5 -1
  147. data/spec/unit/application/device_spec.rb +2 -2
  148. data/spec/unit/application/filebucket_spec.rb +22 -2
  149. data/spec/unit/configurer_spec.rb +1 -1
  150. data/spec/unit/defaults_spec.rb +24 -1
  151. data/spec/unit/environments_spec.rb +8 -0
  152. data/spec/unit/file_system_spec.rb +10 -0
  153. data/spec/unit/http/client_spec.rb +105 -46
  154. data/spec/unit/http/external_client_spec.rb +201 -0
  155. data/spec/unit/http/resolver_spec.rb +20 -0
  156. data/spec/unit/http/service/ca_spec.rb +25 -2
  157. data/spec/unit/http/service/compiler_spec.rb +184 -6
  158. data/spec/unit/http/service/file_server_spec.rb +35 -3
  159. data/spec/unit/http/service/report_spec.rb +3 -1
  160. data/spec/unit/http/service_spec.rb +3 -3
  161. data/spec/unit/http/session_spec.rb +56 -7
  162. data/spec/unit/indirector/file_bucket_file/rest_spec.rb +82 -2
  163. data/spec/unit/network/http/pool_spec.rb +3 -3
  164. data/spec/unit/node/environment_spec.rb +16 -0
  165. data/spec/unit/provider/group/windows_adsi_spec.rb +43 -10
  166. data/spec/unit/provider/package/apt_spec.rb +30 -0
  167. data/spec/unit/provider/package/dnfmodule_spec.rb +33 -14
  168. data/spec/unit/provider/package/gem_spec.rb +40 -0
  169. data/spec/unit/provider/package/pacman_spec.rb +6 -21
  170. data/spec/unit/provider/package/pip_spec.rb +26 -3
  171. data/spec/unit/provider/package/pkgdmg_spec.rb +1 -1
  172. data/spec/unit/provider/package/pkgng_spec.rb +38 -0
  173. data/spec/unit/provider/package/puppet_gem_spec.rb +8 -0
  174. data/spec/unit/provider/package/rpm_spec.rb +0 -212
  175. data/spec/unit/provider/package/yum_spec.rb +235 -1
  176. data/spec/unit/provider/service/systemd_spec.rb +10 -1
  177. data/spec/unit/provider/user/windows_adsi_spec.rb +3 -3
  178. data/spec/unit/puppet_pal_2pec.rb +0 -29
  179. data/spec/unit/reports/http_spec.rb +70 -52
  180. data/spec/unit/ssl/host_spec.rb +4 -2
  181. data/spec/unit/ssl/oids_spec.rb +1 -0
  182. data/spec/unit/ssl/state_machine_spec.rb +38 -6
  183. data/spec/unit/transaction/report_spec.rb +4 -0
  184. data/spec/unit/util/at_fork_spec.rb +2 -2
  185. data/spec/unit/util/package/version/debian_spec.rb +83 -0
  186. data/spec/unit/util/package/version/pip_spec.rb +464 -0
  187. data/spec/unit/util/package/version/range_spec.rb +154 -0
  188. data/spec/unit/util/package/version/rpm_spec.rb +121 -0
  189. data/spec/unit/util/pidlock_spec.rb +83 -47
  190. data/spec/unit/util/rpm_compare_spec.rb +196 -0
  191. data/spec/unit/util/windows/adsi_spec.rb +4 -4
  192. data/spec/unit/util/windows/sid_spec.rb +2 -2
  193. data/tasks/generate_cert_fixtures.rake +15 -1
  194. metadata +51 -6
  195. data/spec/integration/faces/plugin_spec.rb +0 -63
@@ -24,7 +24,7 @@ Puppet::Type.type(:group).provide :windows_adsi do
24
24
  # since the default array_matching comparison is not commutative
25
25
 
26
26
  # dupes automatically weeded out when hashes built
27
- current_members = Puppet::Util::Windows::ADSI::Group.name_sid_hash(current)
27
+ current_members = Puppet::Util::Windows::ADSI::Group.name_sid_hash(current, true)
28
28
  specified_members = Puppet::Util::Windows::ADSI::Group.name_sid_hash(should)
29
29
 
30
30
  current_sids = current_members.keys.to_a
@@ -52,7 +52,7 @@ Puppet::Type.type(:group).provide :windows_adsi do
52
52
  account = sid.account
53
53
  end
54
54
  resource.debug("#{sid.domain}\\#{account} (#{sid.sid})")
55
- "#{sid.domain}\\#{account}"
55
+ sid.domain ? "#{sid.domain}\\#{account}" : account
56
56
  end
57
57
  return users.join(',')
58
58
  end
@@ -66,7 +66,7 @@ Puppet::Type.type(:group).provide :windows_adsi do
66
66
  end
67
67
 
68
68
  def members
69
- @members ||= Puppet::Util::Windows::ADSI::Group.name_sid_hash(group.members)
69
+ @members ||= Puppet::Util::Windows::ADSI::Group.name_sid_hash(group.members, true)
70
70
 
71
71
  # @members.keys returns an array of SIDs. We need to convert those SIDs into
72
72
  # names so that `puppet resource` prints the right output.
@@ -1,7 +1,11 @@
1
+ require 'puppet/util/package/version/range'
2
+ require 'puppet/util/package/version/debian'
3
+
1
4
  Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
2
5
  # Provide sorting functionality
3
6
  include Puppet::Util::Package
4
-
7
+ DebianVersion = Puppet::Util::Package::Version::Debian
8
+ VersionRange = Puppet::Util::Package::Version::Range
5
9
  desc "Package management via `apt-get`.
6
10
 
7
11
  This provider supports the `install_options` attribute, which allows command-line flags to be passed to apt-get.
@@ -44,12 +48,41 @@ Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
44
48
  end
45
49
  end
46
50
 
51
+ def best_version(should_range)
52
+ available_versions = SortedSet.new
53
+
54
+ output = aptcache :madison, @resource[:name]
55
+ output.each_line do |line|
56
+ is = line.split('|')[1].strip
57
+ begin
58
+ is_version = DebianVersion.parse(is)
59
+ available_versions << is_version if should_range.include?(is_version)
60
+ rescue DebianVersion::ValidationFailure
61
+ Puppet.debug("Cannot parse #{is} as a debian version")
62
+ end
63
+ end
64
+
65
+ return available_versions.to_a.last unless available_versions.empty?
66
+
67
+ Puppet.debug("No available version for package #{@resource[:name]} is included in range #{should_range}")
68
+ should_range
69
+ end
70
+
47
71
  # Install a package using 'apt-get'. This function needs to support
48
72
  # installing a specific version.
49
73
  def install
50
74
  self.run_preseed if @resource[:responsefile]
51
75
  should = @resource[:ensure]
52
76
 
77
+ if should.is_a?(String)
78
+ begin
79
+ should_range = VersionRange.parse(should, DebianVersion)
80
+ should = best_version(should_range)
81
+ rescue VersionRange::ValidationFailure, DebianVersion::ValidationFailure
82
+ Puppet.debug("Cannot parse #{should} as a debian version range, falling through")
83
+ end
84
+ end
85
+
53
86
  checkforcdrom
54
87
  cmd = %w{-q -y}
55
88
 
@@ -130,4 +163,31 @@ Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
130
163
  def install_options
131
164
  join_options(@resource[:install_options])
132
165
  end
166
+
167
+ def insync?(is)
168
+ # this is called after the generic version matching logic (insync? for the
169
+ # type), so we only get here if should != is
170
+
171
+ return false unless is && is != :absent
172
+
173
+ #if 'should' is a range and 'is' a debian version we should check if 'should' includes 'is'
174
+ should = @resource[:ensure]
175
+
176
+ return false unless is.is_a?(String) && should.is_a?(String)
177
+
178
+ begin
179
+ should_range = VersionRange.parse(should, DebianVersion)
180
+ rescue VersionRange::ValidationFailure, DebianVersion::ValidationFailure
181
+ Puppet.debug("Cannot parse #{should} as a debian version range")
182
+ return false
183
+ end
184
+
185
+ begin
186
+ is_version = DebianVersion.parse(is)
187
+ rescue DebianVersion::ValidationFailure
188
+ Puppet.debug("Cannot parse #{is} as a debian version")
189
+ return false
190
+ end
191
+ should_range.include?(is_version)
192
+ end
133
193
  end
@@ -34,14 +34,22 @@ Puppet::Type.type(:package).provide :dnfmodule, :parent => :dnf do
34
34
 
35
35
  def self.instances
36
36
  packages = []
37
- cmd = "#{command(:dnf)} module list --installed -d 0 -e #{error_level}"
37
+ cmd = "#{command(:dnf)} module list --enabled -d 0 -e #{error_level}"
38
38
  execute(cmd).each_line do |line|
39
- next unless line =~ /\[i\][, ]/ # get rid of non-package lines (including last Hint line)
40
- line.gsub!(/\[[de]\]/, '') # we don't care about default/enabled flags
39
+ # select only lines with actual packages since DNF clutters the output
40
+ next unless line =~ /\[[ei]\][, ]/
41
+ line.gsub!(/\[d\]/, '') # we don't care about the default flag
42
+
43
+ flavor = if line.include?('[i]')
44
+ line.split('[i]').first.split.last
45
+ else
46
+ :absent
47
+ end
48
+
41
49
  packages << new(
42
50
  name: line.split[0],
43
51
  ensure: line.split[1],
44
- flavor: line.split('[i]').first.split.last, # this is nasty
52
+ flavor: flavor,
45
53
  provider: name
46
54
  )
47
55
  end
@@ -55,28 +63,43 @@ Puppet::Type.type(:package).provide :dnfmodule, :parent => :dnf do
55
63
  pkg ? pkg.properties : nil
56
64
  end
57
65
 
58
- def reset
59
- execute([command(:dnf), 'module', 'reset', '-d', '0', '-e', self.class.error_level, '-y', @resource[:name]])
60
- end
61
-
62
66
  # to install specific streams and profiles:
63
67
  # $ dnf module install module-name:stream/profile
64
68
  # $ dnf module install perl:5.24/minimal
65
69
  # if unspecified, they will be defaulted (see [d] param in dnf module list output)
66
70
  def install
67
- args = @resource[:name]
68
71
  # ensure we start fresh (remove existing stream)
69
72
  uninstall unless [:absent, :purged].include?(@property_hash[:ensure])
73
+
74
+ args = @resource[:name].dup
70
75
  case @resource[:ensure]
71
76
  when true, false, Symbol
72
77
  # pass
73
78
  else
74
79
  args << ":#{@resource[:ensure]}"
75
80
  end
76
- if @resource[:flavor]
77
- args << "/#{@resource[:flavor]}"
81
+ args << "/#{@resource[:flavor]}" if @resource[:flavor]
82
+
83
+ if @resource[:enable_only] == true
84
+ enable(args)
85
+ else
86
+ begin
87
+ execute([command(:dnf), 'module', 'install', '-d', '0', '-e', self.class.error_level, '-y', args])
88
+ rescue Puppet::ExecutionFailure => e
89
+ # module has no default profile and no profile was requested, so just enable the stream
90
+ # DNF versions prior to 4.2.8 do not need this workaround
91
+ # see https://bugzilla.redhat.com/show_bug.cgi?id=1669527
92
+ if @resource[:flavor] == nil && e.message =~ /^missing groups or modules: #{Regexp.quote(@resource[:name])}$/
93
+ enable(args)
94
+ else
95
+ raise
96
+ end
97
+ end
78
98
  end
79
- execute([command(:dnf), 'module', 'install', '-d', '0', '-e', self.class.error_level, '-y', args])
99
+ end
100
+
101
+ def enable(args = @resource[:name])
102
+ execute([command(:dnf), 'module', 'enable', '-d', '0', '-e', self.class.error_level, '-y', args])
80
103
  end
81
104
 
82
105
  def uninstall
@@ -84,6 +107,10 @@ Puppet::Type.type(:package).provide :dnfmodule, :parent => :dnf do
84
107
  reset # reset module to the default stream
85
108
  end
86
109
 
110
+ def reset
111
+ execute([command(:dnf), 'module', 'reset', '-d', '0', '-e', self.class.error_level, '-y', @resource[:name]])
112
+ end
113
+
87
114
  def flavor
88
115
  @property_hash[:flavor]
89
116
  end
@@ -1,3 +1,5 @@
1
+ require 'puppet/util/package/version/gem'
2
+ require 'puppet/util/package/version/range'
1
3
  require 'puppet/provider/package_targetable'
2
4
  require 'uri'
3
5
 
@@ -15,7 +17,10 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package::
15
17
  These options should be specified as an array where each element is either a
16
18
  string or a hash."
17
19
 
18
- has_feature :versionable, :install_options, :uninstall_options, :targetable
20
+ has_feature :versionable, :install_options, :uninstall_options, :targetable, :version_ranges
21
+
22
+ GEM_VERSION = Puppet::Util::Package::Version::Gem
23
+ GEM_VERSION_RANGE = Puppet::Util::Package::Version::Range
19
24
 
20
25
  # Override the specificity method to return 1 if gem is not set as default provider
21
26
  def self.specificity
@@ -125,16 +130,35 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package::
125
130
 
126
131
  def insync?(is)
127
132
  return false unless is && is != :absent
133
+ is = [is] unless is.is_a? Array
134
+ should = @resource[:ensure]
128
135
 
136
+ unless should =~ Regexp.union(/,/, Gem::Requirement::PATTERN)
137
+ begin
138
+ should_range = GEM_VERSION_RANGE.parse(should, GEM_VERSION)
139
+ rescue GEM_VERSION_RANGE::ValidationFailure, GEM_VERSION::ValidationFailure
140
+ Puppet.debug("Cannot parse #{should} as a ruby gem version range")
141
+ return false
142
+ end
143
+
144
+ return is.any? do |version|
145
+ begin
146
+ should_range.include?(GEM_VERSION.parse(version))
147
+ rescue GEM_VERSION::ValidationFailure
148
+ Puppet.debug("Cannot parse #{version} as a ruby gem version")
149
+ false
150
+ end
151
+ end
152
+ end
153
+
129
154
  begin
130
- dependency = Gem::Dependency.new('', resource[:ensure])
155
+ # Range intersections are not supported by Gem::Requirement, so just split by comma.
156
+ dependency = Gem::Dependency.new('', should.split(','))
131
157
  rescue ArgumentError
132
158
  # Bad requirements will cause an error during gem command invocation, so just return not in sync
133
159
  return false
134
160
  end
135
161
 
136
- is = [is] unless is.is_a? Array
137
-
138
162
  # Check if any version matches the dependency
139
163
  is.any? { |version| dependency.match?('', version) }
140
164
  end
@@ -148,12 +172,22 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package::
148
172
  command = resource_or_provider_command
149
173
  command_options = ["install"]
150
174
  command_options += install_options if resource[:install_options]
175
+ should = resource[:ensure]
176
+
177
+ unless should =~ Regexp.union(/,/, Gem::Requirement::PATTERN)
178
+ begin
179
+ should_range = GEM_VERSION_RANGE.parse(should, GEM_VERSION)
180
+ should = should_range.to_gem_version
181
+ useversion = true
182
+ rescue GEM_VERSION_RANGE::ValidationFailure, GEM_VERSION::ValidationFailure
183
+ Puppet.debug("Cannot parse #{should} as a ruby gem version range. Falling through.")
184
+ end
185
+ end
151
186
 
152
187
  if Puppet::Util::Platform.windows?
153
- version = resource[:ensure]
154
- command_options << "-v" << %Q["#{version}"] if (! resource[:ensure].is_a? Symbol) and useversion
188
+ command_options << "-v" << %Q["#{should}"] if useversion && !should.is_a?(Symbol)
155
189
  else
156
- command_options << "-v" << resource[:ensure] if (! resource[:ensure].is_a? Symbol) and useversion
190
+ command_options << "-v" << should if useversion && !should.is_a?(Symbol)
157
191
  end
158
192
 
159
193
  if Puppet::Util::Package.versioncmp(rubygem_version(command), '2.0.0') == -1
@@ -132,9 +132,6 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag
132
132
 
133
133
  # We rescue the main check from Pacman with a check on the AUR using yaourt, if installed
134
134
  def latest
135
- # Synchronize the database
136
- pacman "-Sy"
137
-
138
135
  resource_name = @resource[:name]
139
136
 
140
137
  # If target is a group, construct the group version
@@ -243,7 +240,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag
243
240
  else
244
241
  fail _("Source %{source} is not supported by pacman") % { source: source }
245
242
  end
246
- pacman "--noconfirm", "--noprogressbar", "-Sy"
243
+ pacman "--noconfirm", "--noprogressbar", "-S"
247
244
  pacman "--noconfirm", "--noprogressbar", "-U", source
248
245
  end
249
246
 
@@ -255,7 +252,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag
255
252
 
256
253
  cmd = %w{--noconfirm --needed --noprogressbar}
257
254
  cmd += install_options if @resource[:install_options]
258
- cmd << "-Sy" << resource_name
255
+ cmd << "-S" << resource_name
259
256
 
260
257
  if self.class.yaourt?
261
258
  yaourt(*cmd)
@@ -1,6 +1,8 @@
1
1
  # Puppet package provider for Python's `pip` package management frontend.
2
2
  # <http://pip.pypa.io/>
3
3
 
4
+ require 'puppet/util/package/version/pip'
5
+ require 'puppet/util/package/version/range'
4
6
  require 'puppet/provider/package_targetable'
5
7
  require 'puppet/util/http_proxy'
6
8
 
@@ -11,7 +13,10 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
11
13
  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pip.
12
14
  These options should be specified as an array where each element is either a string or a hash."
13
15
 
14
- has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options, :targetable
16
+ has_feature :installable, :uninstallable, :upgradeable, :versionable, :version_ranges, :install_options, :targetable
17
+
18
+ PIP_VERSION = Puppet::Util::Package::Version::Pip
19
+ PIP_VERSION_RANGE = Puppet::Util::Package::Version::Range
15
20
 
16
21
  # Override the specificity method to return 1 if pip is not set as default provider
17
22
  def self.specificity
@@ -25,7 +30,6 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
25
30
 
26
31
  # Define the default provider package command name when the provider is targetable.
27
32
  # Required by Puppet::Provider::Package::Targetable::resource_or_provider_command
28
-
29
33
  def self.provider_command
30
34
  # Ensure pip can upgrade pip, which usually puts pip into a new path /usr/local/bin/pip (compared to /usr/bin/pip)
31
35
  self.cmd.map { |c| which(c) }.find { |c| c != nil }
@@ -58,7 +62,6 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
58
62
 
59
63
  # Return an array of structured information about every installed package
60
64
  # that's managed by `pip` or an empty array if `pip` is not available.
61
-
62
65
  def self.instances(target_command = nil)
63
66
  if target_command
64
67
  command = target_command
@@ -72,7 +75,7 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
72
75
 
73
76
  command_options = ['freeze']
74
77
  command_version = self.pip_version(command)
75
- if Puppet::Util::Package.versioncmp(command_version, '8.1.0') >= 0
78
+ if compare_pip_versions(command_version, '8.1.0') >= 0
76
79
  command_options << '--all'
77
80
  end
78
81
 
@@ -88,7 +91,7 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
88
91
  # Pip can also upgrade pip, but it's not listed in freeze so need to special case it
89
92
  # Pip list would also show pip installed version, but "pip list" doesn't exist for older versions of pip (E.G v1.0)
90
93
  # Not needed when "pip freeze --all" is available.
91
- if Puppet::Util::Package.versioncmp(command_version, '8.1.0') == -1
94
+ if compare_pip_versions(command_version, '8.1.0') == -1
92
95
  packages << new({:ensure => command_version, :name => File.basename(command), :provider => name, :command => command})
93
96
  end
94
97
 
@@ -105,7 +108,6 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
105
108
 
106
109
  # Return structured information about a particular package or `nil`
107
110
  # if the package is not installed or `pip` itself is not available.
108
-
109
111
  def query
110
112
  command = resource_or_provider_command
111
113
  self.class.validate_command(command)
@@ -116,24 +118,43 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
116
118
  return nil
117
119
  end
118
120
 
119
- # Use pip CLI to look up versions from PyPI repositories,
120
- # honoring local pip config such as custom repositories.
121
-
121
+ # Return latest version available for current package
122
122
  def latest
123
123
  command = resource_or_provider_command
124
124
  self.class.validate_command(command)
125
125
 
126
126
  command_version = self.class.pip_version(command)
127
- if Puppet::Util::Package.versioncmp(command_version, '1.5.4') == -1
128
- latest_with_old_pip
127
+ if self.class.compare_pip_versions(command_version, '1.5.4') == -1
128
+ available_versions_with_old_pip.last
129
129
  else
130
- latest_with_new_pip
130
+ available_versions_with_new_pip.last
131
+ end
132
+ end
133
+
134
+ def self.compare_pip_versions(x, y)
135
+ begin
136
+ Puppet::Util::Package::Version::Pip.compare(x, y)
137
+ rescue PIP_VERSION::ValidationFailure => ex
138
+ Puppet.debug("Cannot compare #{x} and #{y}. #{ex.message} Falling through default comparison mechanism.")
139
+ Puppet::Util::Package.versioncmp(x, y)
131
140
  end
132
141
  end
133
142
 
134
- # Less resource-intensive approach for pip version 1.5.4 and newer.
143
+ # Use pip CLI to look up versions from PyPI repositories,
144
+ # honoring local pip config such as custom repositories.
145
+ def available_versions
146
+ command = resource_or_provider_command
147
+ self.class.validate_command(command)
148
+
149
+ command_version = self.class.pip_version(command)
150
+ if self.class.compare_pip_versions(command_version, '1.5.4') == -1
151
+ available_versions_with_old_pip
152
+ else
153
+ available_versions_with_new_pip
154
+ end
155
+ end
135
156
 
136
- def latest_with_new_pip
157
+ def available_versions_with_new_pip
137
158
  command = resource_or_provider_command
138
159
  self.class.validate_command(command)
139
160
 
@@ -142,21 +163,18 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
142
163
  execpipe command_and_options do |process|
143
164
  process.collect do |line|
144
165
  # PIP OUTPUT: Could not find a version that satisfies the requirement example==versionplease (from versions: 1.2.3, 4.5.6)
145
- if line =~ /from versions: /
146
- textAfterLastMatch = $'.chomp(")\n")
147
- versionList = textAfterLastMatch.split(', ').sort do |x,y|
148
- Puppet::Util::Package.versioncmp(x, y)
166
+ if line =~ /from versions: (.+)\)/
167
+ versionList = $1.split(', ').sort do |x,y|
168
+ self.class.compare_pip_versions(x, y)
149
169
  end
150
- return versionList.last
170
+ return versionList
151
171
  end
152
172
  end
153
- return nil
154
173
  end
174
+ []
155
175
  end
156
176
 
157
- # More resource-intensive approach for pip version 1.5.3 and older.
158
-
159
- def latest_with_old_pip
177
+ def available_versions_with_old_pip
160
178
  command = resource_or_provider_command
161
179
  self.class.validate_command(command)
162
180
 
@@ -166,38 +184,73 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
166
184
  execpipe command_and_options do |process|
167
185
  process.collect do |line|
168
186
  # PIP OUTPUT: Using version 0.10.1 (newest of versions: 1.2.3, 4.5.6)
169
- if line =~ /Using version (.+?) \(newest of versions/
170
- return $1
187
+ if line =~ /Using version .+? \(newest of versions: (.+?)\)/
188
+ versionList = $1.split(', ').sort do |x,y|
189
+ self.class.compare_pip_versions(x, y)
190
+ end
191
+ return versionList
171
192
  end
172
193
  end
173
- return nil
174
194
  end
195
+ return []
175
196
  end
176
197
  end
177
198
 
199
+ # Finds the most suitable version available in a given range
200
+ def best_version(should_range)
201
+ included_available_versions = []
202
+ available_versions.each do |version|
203
+ version = PIP_VERSION.parse(version)
204
+ included_available_versions.push(version) if should_range.include?(version)
205
+ end
206
+
207
+ included_available_versions.sort!
208
+ return included_available_versions.last unless included_available_versions.empty?
209
+
210
+ Puppet.debug("No available version for package #{@resource[:name]} is included in range #{should_range}")
211
+ should_range
212
+ end
213
+
178
214
  # Install a package. The ensure parameter may specify installed,
179
215
  # latest, a version number, or, in conjunction with the source
180
216
  # parameter, an SCM revision. In that case, the source parameter
181
217
  # gives the fully-qualified URL to the repository.
182
-
183
218
  def install
184
219
  command = resource_or_provider_command
185
220
  self.class.validate_command(command)
186
221
 
222
+ should = @resource[:ensure]
187
223
  command_options = %w{install -q}
188
224
  command_options += install_options if @resource[:install_options]
189
225
  if @resource[:source]
190
- if String === @resource[:ensure]
191
- command_options << "#{@resource[:source]}@#{@resource[:ensure]}#egg=#{@resource[:name]}"
226
+ if String === should
227
+ command_options << "#{@resource[:source]}@#{should}#egg=#{@resource[:name]}"
192
228
  else
193
229
  command_options << "#{@resource[:source]}#egg=#{@resource[:name]}"
194
230
  end
195
231
  else
196
- case @resource[:ensure]
197
- when String
198
- command_options << "#{@resource[:name]}==#{@resource[:ensure]}"
232
+ case should
199
233
  when :latest
200
234
  command_options << "--upgrade" << @resource[:name]
235
+ when String
236
+ begin
237
+ should_range = PIP_VERSION_RANGE.parse(should, PIP_VERSION)
238
+ should = best_version(should_range)
239
+
240
+ unless should == should_range
241
+ command_options << "#{@resource[:name]}==#{should}"
242
+ else
243
+ # when no suitable version for the given range was found, let pip handle
244
+ if should.is_a?(PIP_VERSION_RANGE::MinMax)
245
+ command_options << "#{@resource[:name]} #{should.split.join(',')}"
246
+ else
247
+ command_options << "#{@resource[:name]} #{should}"
248
+ end
249
+ end
250
+ rescue PIP_VERSION_RANGE::ValidationFailure, PIP_VERSION::ValidationFailure
251
+ Puppet.debug("Cannot parse #{should} as a pip version range, falling through.")
252
+ command_options << "#{@resource[:name]}==#{should}"
253
+ end
201
254
  else
202
255
  command_options << @resource[:name]
203
256
  end
@@ -208,7 +261,6 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
208
261
 
209
262
  # Uninstall a package. Uninstall won't work reliably on Debian/Ubuntu unless this issue gets fixed.
210
263
  # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562544
211
-
212
264
  def uninstall
213
265
  command = resource_or_provider_command
214
266
  self.class.validate_command(command)
@@ -226,6 +278,26 @@ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package
226
278
  join_options(@resource[:install_options])
227
279
  end
228
280
 
281
+ def insync?(is)
282
+ return false unless is && is != :absent
283
+ begin
284
+ should = @resource[:ensure]
285
+ should_range = PIP_VERSION_RANGE.parse(should, PIP_VERSION)
286
+ rescue PIP_VERSION_RANGE::ValidationFailure, PIP_VERSION::ValidationFailure
287
+ Puppet.debug("Cannot parse #{should} as a pip version range")
288
+ return false
289
+ end
290
+
291
+ begin
292
+ is_version = PIP_VERSION.parse(is)
293
+ rescue PIP_VERSION::ValidationFailure
294
+ Puppet.debug("Cannot parse #{is} as a pip version")
295
+ return false
296
+ end
297
+
298
+ should_range.include?(is_version)
299
+ end
300
+
229
301
  def self.quote(path)
230
302
  if path.include?(" ")
231
303
  "\"#{path}\""