puppet 6.4.2-universal-darwin → 6.4.3-universal-darwin

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +17 -17
  3. data/ext/solaris/smf/puppet.xml +2 -0
  4. data/lib/hiera/scope.rb +7 -0
  5. data/lib/puppet.rb +1 -1
  6. data/lib/puppet/application/agent.rb +16 -5
  7. data/lib/puppet/application/device.rb +12 -3
  8. data/lib/puppet/application/ssl.rb +2 -0
  9. data/lib/puppet/configurer.rb +1 -1
  10. data/lib/puppet/network/http/factory.rb +5 -0
  11. data/lib/puppet/pops/types/types.rb +5 -3
  12. data/lib/puppet/provider.rb +1 -2
  13. data/lib/puppet/provider/package.rb +2 -0
  14. data/lib/puppet/provider/package/dpkg.rb +15 -2
  15. data/lib/puppet/provider/package/gem.rb +65 -29
  16. data/lib/puppet/provider/package/pip.rb +135 -111
  17. data/lib/puppet/provider/package/pip3.rb +1 -1
  18. data/lib/puppet/provider/package/puppet_gem.rb +1 -1
  19. data/lib/puppet/provider/package/rpm.rb +27 -16
  20. data/lib/puppet/provider/package/yum.rb +1 -1
  21. data/lib/puppet/provider/package_targetable.rb +68 -0
  22. data/lib/puppet/provider/service/upstart.rb +5 -3
  23. data/lib/puppet/provider/user/useradd.rb +16 -13
  24. data/lib/puppet/settings/server_list_setting.rb +9 -0
  25. data/lib/puppet/ssl/host.rb +1 -1
  26. data/lib/puppet/ssl/state_machine.rb +99 -28
  27. data/lib/puppet/type/package.rb +46 -9
  28. data/lib/puppet/util/pidlock.rb +3 -2
  29. data/lib/puppet/util/windows/process.rb +8 -8
  30. data/lib/puppet/util/windows/registry.rb +7 -1
  31. data/lib/puppet/util/windows/user.rb +14 -4
  32. data/lib/puppet/version.rb +1 -1
  33. data/locales/puppet.pot +115 -103
  34. data/man/man5/puppet.conf.5 +2 -2
  35. data/man/man8/puppet-agent.8 +1 -1
  36. data/man/man8/puppet-apply.8 +1 -1
  37. data/man/man8/puppet-catalog.8 +1 -1
  38. data/man/man8/puppet-config.8 +1 -1
  39. data/man/man8/puppet-describe.8 +1 -1
  40. data/man/man8/puppet-device.8 +1 -1
  41. data/man/man8/puppet-doc.8 +1 -1
  42. data/man/man8/puppet-epp.8 +1 -1
  43. data/man/man8/puppet-facts.8 +1 -1
  44. data/man/man8/puppet-filebucket.8 +1 -1
  45. data/man/man8/puppet-generate.8 +1 -1
  46. data/man/man8/puppet-help.8 +1 -1
  47. data/man/man8/puppet-key.8 +1 -1
  48. data/man/man8/puppet-lookup.8 +1 -1
  49. data/man/man8/puppet-man.8 +1 -1
  50. data/man/man8/puppet-module.8 +1 -1
  51. data/man/man8/puppet-node.8 +1 -1
  52. data/man/man8/puppet-parser.8 +1 -1
  53. data/man/man8/puppet-plugin.8 +1 -1
  54. data/man/man8/puppet-report.8 +1 -1
  55. data/man/man8/puppet-resource.8 +1 -1
  56. data/man/man8/puppet-script.8 +1 -1
  57. data/man/man8/puppet-ssl.8 +1 -1
  58. data/man/man8/puppet-status.8 +1 -1
  59. data/man/man8/puppet.8 +2 -2
  60. data/spec/integration/type/package_spec.rb +1 -1
  61. data/spec/integration/util/windows/registry_spec.rb +52 -0
  62. data/spec/integration/util/windows/user_spec.rb +19 -0
  63. data/spec/unit/application/agent_spec.rb +53 -32
  64. data/spec/unit/application/ssl_spec.rb +13 -0
  65. data/spec/unit/configurer_spec.rb +1 -1
  66. data/spec/unit/functions/new_spec.rb +15 -0
  67. data/spec/unit/hiera/scope_spec.rb +7 -0
  68. data/spec/unit/network/http/factory_spec.rb +6 -0
  69. data/spec/unit/provider/package/dpkg_spec.rb +18 -1
  70. data/spec/unit/provider/package/gem_spec.rb +101 -48
  71. data/spec/unit/provider/package/pip3_spec.rb +17 -0
  72. data/spec/unit/provider/package/pip_spec.rb +57 -67
  73. data/spec/unit/provider/package/puppet_gem_spec.rb +22 -6
  74. data/spec/unit/provider/package/rpm_spec.rb +116 -27
  75. data/spec/unit/provider/service/upstart_spec.rb +3 -22
  76. data/spec/unit/settings/server_list_setting_spec.rb +21 -0
  77. data/spec/unit/ssl/state_machine_spec.rb +206 -83
  78. data/spec/unit/util/pidlock_spec.rb +26 -0
  79. metadata +5 -2
@@ -1,54 +1,34 @@
1
1
  # Puppet package provider for Python's `pip` package management frontend.
2
2
  # <http://pip.pypa.io/>
3
3
 
4
- require 'puppet/provider/package'
4
+ require 'puppet/provider/package_targetable'
5
5
  require 'puppet/util/http_proxy'
6
6
 
7
- Puppet::Type.type(:package).provide :pip,
8
- :parent => ::Puppet::Provider::Package do
7
+ Puppet::Type.type(:package).provide :pip, :parent => ::Puppet::Provider::Package::Targetable do
9
8
 
10
9
  desc "Python packages via `pip`.
11
10
 
12
11
  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pip.
13
12
  These options should be specified as an array where each element is either a string or a hash."
14
13
 
15
- has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options
14
+ has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options, :targetable
16
15
 
17
- # Parse lines of output from `pip freeze`, which are structured as
18
- # _package_==_version_.
19
- def self.parse(line)
20
- if line.chomp =~ /^([^=]+)==([^=]+)$/
21
- {:ensure => $2, :name => $1, :provider => name}
22
- else
23
- nil
24
- end
25
- end
16
+ # Override the specificity method to return 1 if pip is not set as default provider
17
+ def self.specificity
18
+ match = default_match
19
+ length = match ? match.length : 0
26
20
 
27
- # Return an array of structured information about every installed package
28
- # that's managed by `pip` or an empty array if `pip` is not available.
29
- def self.instances
30
- packages = []
31
- pip_cmd = self.pip_cmd
32
- return [] unless pip_cmd
33
- command = [pip_cmd, 'freeze']
34
- if Puppet::Util::Package.versioncmp(self.pip_version, '8.1.0') >= 0 # a >= b
35
- command << '--all'
36
- end
37
- execpipe command do |process|
38
- process.collect do |line|
39
- next unless options = parse(line)
40
- packages << new(options)
41
- end
42
- end
21
+ return 1 if length == 0
43
22
 
44
- # Pip can also upgrade pip, but it's not listed in freeze so need to special case it
45
- # Pip list would also show pip installed version, but "pip list" doesn't exist for older versions of pip (E.G v1.0)
46
- # Not needed when "pip freeze --all" is available
47
- if Puppet::Util::Package.versioncmp(self.pip_version, '8.1.0') == -1 && version = self.pip_version
48
- packages << new({:ensure => version, :name => File.basename(pip_cmd), :provider => name})
49
- end
23
+ super
24
+ end
50
25
 
51
- packages
26
+ # Define the default provider package command name when the provider is targetable.
27
+ # Required by Puppet::Provider::Package::Targetable::resource_or_provider_command
28
+
29
+ def self.provider_command
30
+ # Ensure pip can upgrade pip, which usually puts pip into a new path /usr/local/bin/pip (compared to /usr/bin/pip)
31
+ self.cmd.map { |c| which(c) }.find { |c| c != nil }
52
32
  end
53
33
 
54
34
  def self.cmd
@@ -59,104 +39,94 @@ Puppet::Type.type(:package).provide :pip,
59
39
  end
60
40
  end
61
41
 
62
- def self.pip_cmd
63
- self.cmd.map { |c| which(c) }.find { |c| c != nil }
64
- end
65
-
66
- def self.pip_version
67
- pip_cmd = self.pip_cmd
68
- return nil unless pip_cmd
69
-
70
- execpipe [pip_cmd, '--version'] do |process|
42
+ def self.pip_version(command)
43
+ execpipe [command, '--version'] do |process|
71
44
  process.collect do |line|
72
45
  return line.strip.match(/^pip (\d+\.\d+\.?\d*).*$/)[1]
73
46
  end
74
47
  end
75
48
  end
76
49
 
77
- # Return structured information about a particular package or `nil` if
78
- # it is not installed or `pip` itself is not available.
79
- def query
80
- self.class.instances.each do |provider_pip|
81
- return provider_pip.properties if @resource[:name].downcase == provider_pip.name.downcase
50
+ # Return an array of structured information about every installed package
51
+ # that's managed by `pip` or an empty array if `pip` is not available.
52
+
53
+ def self.instances(target_command = nil)
54
+ if target_command
55
+ command = target_command
56
+ self.validate_command(command)
57
+ else
58
+ command = provider_command
82
59
  end
83
- return nil
84
- end
85
60
 
86
- # Use pip CLI to look up versions from PyPI repositories, honoring local pip config such as custom repositories
87
- def latest
88
- return nil unless self.class.pip_cmd
89
- if Puppet::Util::Package.versioncmp(self.class.pip_version, '1.5.4') == -1 # a < b
90
- return latest_with_old_pip
61
+ packages = []
62
+ return packages unless command
63
+
64
+ command_options = ['freeze']
65
+ command_version = self.pip_version(command)
66
+ if Puppet::Util::Package.versioncmp(command_version, '8.1.0') >= 0
67
+ command_options << '--all'
91
68
  end
92
- latest_with_new_pip
93
- end
94
69
 
95
- # Install a package. The ensure parameter may specify installed,
96
- # latest, a version number, or, in conjunction with the source
97
- # parameter, an SCM revision. In that case, the source parameter
98
- # gives the fully-qualified URL to the repository.
99
- def install
100
- args = %w{install -q}
101
- args += install_options if @resource[:install_options]
102
- if @resource[:source]
103
- if String === @resource[:ensure]
104
- args << "#{@resource[:source]}@#{@resource[:ensure]}#egg=#{
105
- @resource[:name]}"
106
- else
107
- args << "#{@resource[:source]}#egg=#{@resource[:name]}"
108
- end
109
- else
110
- case @resource[:ensure]
111
- when String
112
- args << "#{@resource[:name]}==#{@resource[:ensure]}"
113
- when :latest
114
- args << "--upgrade" << @resource[:name]
115
- else
116
- args << @resource[:name]
70
+ execpipe [command, command_options] do |process|
71
+ process.collect do |line|
72
+ next unless pkg = parse(line)
73
+ pkg[:command] = command
74
+ packages << new(pkg)
117
75
  end
118
76
  end
119
- lazy_pip(*args)
120
- end
121
77
 
122
- # Uninstall a package. Uninstall won't work reliably on Debian/Ubuntu
123
- # unless this issue gets fixed.
124
- # <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562544>
125
- def uninstall
126
- lazy_pip "uninstall", "-y", "-q", @resource[:name]
78
+ # Pip can also upgrade pip, but it's not listed in freeze so need to special case it
79
+ # Pip list would also show pip installed version, but "pip list" doesn't exist for older versions of pip (E.G v1.0)
80
+ # Not needed when "pip freeze --all" is available.
81
+ if Puppet::Util::Package.versioncmp(command_version, '8.1.0') == -1
82
+ packages << new({:ensure => command_version, :name => File.basename(command), :provider => name, :command => command})
83
+ end
84
+
85
+ packages
127
86
  end
128
87
 
129
- def update
130
- install
88
+ # Parse lines of output from `pip freeze`, which are structured as:
89
+ # _package_==_version_
90
+ def self.parse(line)
91
+ if line.chomp =~ /^([^=]+)==([^=]+)$/
92
+ {:ensure => $2, :name => $1, :provider => name}
93
+ end
131
94
  end
132
95
 
133
- # Execute a `pip` command. If Puppet doesn't yet know how to do so,
134
- # try to teach it and if even that fails, raise the error.
135
- private
136
- def lazy_pip(*args)
137
- pip(*args)
138
- rescue NoMethodError => e
139
- # Ensure pip can upgrade pip, which usually puts pip into a new path /usr/local/bin/pip (compared to /usr/bin/pip)
140
- # The path to pip needs to be looked up again in the subsequent request. Using the preferred approach as noted
141
- # in provider.rb ensures this (copied below for reference)
142
- #
143
- # @note From provider.rb; It is preferred if the commands are not entered with absolute paths as this allows puppet
144
- # to search for them using the PATH variable.
145
- if pathname = self.class.cmd.map { |c| which(c) }.find { |c| c != nil }
146
- self.class.commands :pip => File.basename(pathname)
147
- pip(*args)
148
- else
149
- raise e, "Could not locate command #{self.class.cmd.join(' and ')}.", e.backtrace
96
+ # Return structured information about a particular package or `nil`
97
+ # if the package is not installed or `pip` itself is not available.
98
+
99
+ def query
100
+ command = resource_or_provider_command
101
+ self.class.validate_command(command)
102
+
103
+ self.class.instances(command).each do |pkg|
104
+ return pkg.properties if @resource[:name].downcase == pkg.name.downcase
150
105
  end
106
+ return nil
151
107
  end
152
108
 
153
- def install_options
154
- join_options(@resource[:install_options])
109
+ # Use pip CLI to look up versions from PyPI repositories,
110
+ # honoring local pip config such as custom repositories.
111
+
112
+ def latest
113
+ command = resource_or_provider_command
114
+ self.class.validate_command(command)
115
+
116
+ command_version = self.class.pip_version(command)
117
+ if Puppet::Util::Package.versioncmp(command_version, '1.5.4') == -1
118
+ latest_with_old_pip
119
+ else
120
+ latest_with_new_pip
121
+ end
155
122
  end
156
123
 
157
124
  def latest_with_new_pip
125
+ command = resource_or_provider_command
126
+ self.class.validate_command(command)
127
+
158
128
  # Less resource intensive approach for pip version 1.5.4 and above
159
- execpipe ["#{self.class.pip_cmd}", "install", "#{@resource[:name]}==versionplease"] do |process|
129
+ execpipe [command, "install", "#{@resource[:name]}==versionplease"] do |process|
160
130
  process.collect do |line|
161
131
  # PIP OUTPUT: Could not find a version that satisfies the requirement Django==versionplease (from versions: 1.1.3, 1.8rc1)
162
132
  if line =~ /from versions: /
@@ -172,8 +142,11 @@ Puppet::Type.type(:package).provide :pip,
172
142
  end
173
143
 
174
144
  def latest_with_old_pip
145
+ command = resource_or_provider_command
146
+ self.class.validate_command(command)
147
+
175
148
  Dir.mktmpdir("puppet_pip") do |dir|
176
- execpipe ["#{self.class.pip_cmd}", "install", "#{@resource[:name]}", "-d", "#{dir}", "-v"] do |process|
149
+ execpipe [command, "install", "#{@resource[:name]}", "-d", "#{dir}", "-v"] do |process|
177
150
  process.collect do |line|
178
151
  # 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)
179
152
  if line =~ /Using version (.+?) \(newest of versions/
@@ -184,4 +157,55 @@ Puppet::Type.type(:package).provide :pip,
184
157
  end
185
158
  end
186
159
  end
160
+
161
+ # Install a package. The ensure parameter may specify installed,
162
+ # latest, a version number, or, in conjunction with the source
163
+ # parameter, an SCM revision. In that case, the source parameter
164
+ # gives the fully-qualified URL to the repository.
165
+
166
+ def install
167
+ command = resource_or_provider_command
168
+ self.class.validate_command(command)
169
+
170
+ command_options = %w{install -q}
171
+ command_options += install_options if @resource[:install_options]
172
+ if @resource[:source]
173
+ if String === @resource[:ensure]
174
+ command_options << "#{@resource[:source]}@#{@resource[:ensure]}#egg=#{@resource[:name]}"
175
+ else
176
+ command_options << "#{@resource[:source]}#egg=#{@resource[:name]}"
177
+ end
178
+ else
179
+ case @resource[:ensure]
180
+ when String
181
+ command_options << "#{@resource[:name]}==#{@resource[:ensure]}"
182
+ when :latest
183
+ command_options << "--upgrade" << @resource[:name]
184
+ else
185
+ command_options << @resource[:name]
186
+ end
187
+ end
188
+
189
+ execute([command, command_options])
190
+ end
191
+
192
+ # Uninstall a package. Uninstall won't work reliably on Debian/Ubuntu unless this issue gets fixed.
193
+ # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562544
194
+
195
+ def uninstall
196
+ command = resource_or_provider_command
197
+ self.class.validate_command(command)
198
+
199
+ command_options = ["uninstall", "-y", "-q", @resource[:name]]
200
+
201
+ execute([command, command_options])
202
+ end
203
+
204
+ def update
205
+ install
206
+ end
207
+
208
+ def install_options
209
+ join_options(@resource[:install_options])
210
+ end
187
211
  end
@@ -11,7 +11,7 @@ Puppet::Type.type(:package).provide :pip3,
11
11
  This provider supports the `install_options` attribute, which allows command-line flags to be passed to pip3.
12
12
  These options should be specified as an array where each element is either a string or a hash."
13
13
 
14
- has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options
14
+ has_feature :installable, :uninstallable, :upgradeable, :versionable, :install_options, :targetable
15
15
 
16
16
  def self.cmd
17
17
  ["pip3"]
@@ -1,4 +1,4 @@
1
- require 'puppet/provider/package'
1
+ require 'puppet/provider/package/gem'
2
2
 
3
3
  Puppet::Type.type(:package).provide :puppet_gem, :parent => :gem do
4
4
  desc "Puppet Ruby Gem support. This provider is useful for managing
@@ -154,28 +154,39 @@ These options should be specified as an array where each element is either a str
154
154
  end
155
155
 
156
156
  def uninstall
157
- query if get(:arch) == :absent
158
- nvr = "#{get(:name)}-#{get(:version)}-#{get(:release)}"
159
- arch = ".#{get(:arch)}"
160
- # If they specified an arch in the manifest, erase that Otherwise,
161
- # erase the arch we got back from the query. If multiple arches are
162
- # installed and only the package name is specified (without the
163
- # arch), this will uninstall all of them on successive runs of the
164
- # client, one after the other
165
-
166
- # version of RPM prior to 4.2.1 can't accept the architecture as
167
- # part of the package name.
168
- unless Puppet::Util::Package.versioncmp(self.class.current_version, '4.2.1') < 0
169
- if @resource[:name][-arch.size, arch.size] == arch
170
- nvr += arch
157
+ query
158
+ # If version and release (or only version) is specified in the resource,
159
+ # uninstall using them, otherwise uninstall using only the name of the package.
160
+ name = get(:name)
161
+ version = get(:version)
162
+ release = get(:release)
163
+ nav = "#{name}-#{version}"
164
+ nvr = "#{nav}-#{release}"
165
+ if @resource[:name].start_with? nvr
166
+ identifier = nvr
167
+ else
168
+ if @resource[:name].start_with? nav
169
+ identifier = nav
171
170
  else
172
- nvr += ".#{get(:arch)}"
171
+ identifier = name
172
+ end
173
+ end
174
+ # If an arch is specified in the resource, uninstall that arch,
175
+ # otherwise uninstall the arch returned by query.
176
+ # If multiple arches are installed and arch is not specified,
177
+ # this will uninstall all of them after successive runs.
178
+ #
179
+ # rpm prior to 4.2.1 cannot accept architecture as part of the package name.
180
+ unless Puppet::Util::Package.versioncmp(self.class.current_version, '4.2.1') < 0
181
+ arch = ".#{get(:arch)}"
182
+ if @resource[:name].end_with? arch
183
+ identifier += arch
173
184
  end
174
185
  end
175
186
 
176
187
  flag = ['-e']
177
188
  flag += uninstall_options if resource[:uninstall_options]
178
- rpm flag, nvr
189
+ rpm flag, identifier
179
190
  end
180
191
 
181
192
  def update
@@ -107,7 +107,7 @@ defaultfor :osfamily => :redhat, :operatingsystemmajrelease => (4..7).to_a
107
107
  end
108
108
 
109
109
  def self.update_to_hash(pkgname, pkgversion)
110
-
110
+
111
111
  # The pkgname string has two parts: name, and architecture. Architecture
112
112
  # is the portion of the string following the last "." character. All
113
113
  # characters preceding the final dot are the package name. Parse out
@@ -0,0 +1,68 @@
1
+ # Targetable package providers implement a `command` attribute.
2
+ #
3
+ # The `packages` hash passed to `Puppet::Provider::Package::prefetch` is deduplicated,
4
+ # as it is keyed only by name in `Puppet::Transaction::prefetch_if_necessary`.
5
+ #
6
+ # (The `packages` hash passed to ``Puppet::Provider::Package::prefetch`` should be keyed by all namevars,
7
+ # possibly via a `prefetchV2` method that could take a better data structure.)
8
+ #
9
+ # In addition, `Puppet::Provider::Package::properties` calls `query` in the provider.
10
+ require 'puppet/provider/package'
11
+
12
+ # But `query` in the provider depends upon whether a `command` attribute is defined for the resource.
13
+ # This is a Catch-22.
14
+ #
15
+ # Instead ...
16
+ #
17
+ # Inspect any package to access the catalog (every package includes a reference to the catalog).
18
+ # Inspect the catalog to find all of the `command` attributes for all of the packages of this class.
19
+ # Find all of the package instances using each package `command`, including the default provider command.
20
+ # Assign each instance's `provider` by selecting it from the `packages` hash passed to `prefetch`, based upon `name` and `command`.
21
+ #
22
+ # The original `command` parameter in the catalog is not populated by the default (`:default`) for the parameter in type/package.rb.
23
+ # Rather, the result of the `original_parameters` is `nil` when the `command` parameter is undefined in the catalog.
24
+
25
+ class Puppet::Provider::Package::Targetable < Puppet::Provider::Package
26
+ # Prefetch our package list, yo.
27
+ def self.prefetch(packages)
28
+ catalog_packages = packages.first[1]::catalog::resources.select{ |p| p.provider.class == self }
29
+ package_commands = catalog_packages.map { |catalog_package| catalog_package::original_parameters[:command] }.uniq
30
+ package_commands.each do |command|
31
+ instances(command).each do |instance|
32
+ catalog_packages.each do |catalog_package|
33
+ if catalog_package[:name] == instance.name && catalog_package::original_parameters[:command] == command
34
+ catalog_package.provider = instance
35
+ self.debug "Prefetched instance: %{name} via command: %{command}" % { name: instance.name, cmd: (command || :default)}
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # Returns the resource command or provider command.
43
+
44
+ def resource_or_provider_command
45
+ resource::original_parameters[:command] || self.class.provider_command
46
+ end
47
+
48
+ # Targetable providers use has_command/is_optional to defer validation of provider suitability.
49
+ # Evaluate provider suitability here and now by validating that the command is defined and exists.
50
+ #
51
+ # cmd: the full path to the package command.
52
+
53
+ def self.validate_command(cmd)
54
+ unless cmd
55
+ raise Puppet::Error, _("Provider %{name} package command is not functional on this host") % { name: name }
56
+ end
57
+ unless File.file?(cmd)
58
+ raise Puppet::Error, _("Provider %{name} package command '%{cmd}' does not exist on this host") % { name: name, cmd: cmd }
59
+ end
60
+ end
61
+
62
+ # Return information about the package, its provider, and its (optional) command.
63
+
64
+ def to_s
65
+ cmd = resource[:command] || :default
66
+ "#{@resource}(provider=#{self.class.name})(command=#{cmd})"
67
+ end
68
+ end