puppet 5.5.8 → 5.5.10
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +14 -14
- data/Rakefile +3 -1
- data/ext/solaris/smf/svc-puppetd +8 -1
- data/ext/solaris/smf/svc-puppetmasterd +8 -1
- data/lib/puppet/application/device.rb +45 -29
- data/lib/puppet/face/config.rb +1 -1
- data/lib/puppet/forge.rb +4 -1
- data/lib/puppet/pops/evaluator/collectors/abstract_collector.rb +3 -1
- data/lib/puppet/pops/types/p_timespan_type.rb +2 -2
- data/lib/puppet/pops/types/string_converter.rb +11 -2
- data/lib/puppet/provider/cron/crontab.rb +1 -1
- data/lib/puppet/provider/package/windows/exe_package.rb +13 -0
- data/lib/puppet/provider/package/windows/msi_package.rb +8 -0
- data/lib/puppet/provider/package/windows/package.rb +9 -1
- data/lib/puppet/provider/parsedfile.rb +25 -4
- data/lib/puppet/provider/service/smf.rb +54 -0
- data/lib/puppet/transaction.rb +4 -1
- data/lib/puppet/transaction/event_manager.rb +13 -1
- data/lib/puppet/transaction/resource_harness.rb +3 -1
- data/lib/puppet/util/command_line.rb +2 -3
- data/lib/puppet/util/filetype.rb +36 -4
- data/lib/puppet/util/selinux.rb +1 -1
- data/lib/puppet/util/windows/api_types.rb +1 -1
- data/lib/puppet/util/windows/registry.rb +29 -5
- data/lib/puppet/util/windows/service.rb +106 -99
- data/lib/puppet/version.rb +1 -1
- data/locales/puppet.pot +97 -69
- data/man/man5/puppet.conf.5 +2 -2
- data/man/man8/puppet-agent.8 +1 -1
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-ca.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-cert.8 +1 -1
- data/man/man8/puppet-certificate.8 +1 -1
- data/man/man8/puppet-certificate_request.8 +1 -1
- data/man/man8/puppet-certificate_revocation_list.8 +1 -1
- data/man/man8/puppet-config.8 +2 -2
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +23 -19
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +1 -1
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-key.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-man.8 +1 -1
- data/man/man8/puppet-master.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/unit/provider/service/smf/svcs_fmri.out +6 -0
- data/spec/fixtures/unit/provider/service/smf/svcs_multiple_fmris.out +13 -0
- data/spec/integration/ssl/key_spec.rb +0 -4
- data/spec/integration/transaction_spec.rb +1 -1
- data/spec/integration/util/windows/registry_spec.rb +39 -0
- data/spec/unit/application/device_spec.rb +4 -1
- data/spec/unit/application/lookup_spec.rb +10 -9
- data/spec/unit/forge/forge_spec.rb +4 -2
- data/spec/unit/indirector/yaml_spec.rb +1 -1
- data/spec/unit/pops/loaders/loader_spec.rb +6 -7
- data/spec/unit/pops/types/p_timespan_type_spec.rb +22 -0
- data/spec/unit/pops/types/p_timestamp_type_spec.rb +19 -0
- data/spec/unit/pops/types/string_converter_spec.rb +20 -0
- data/spec/unit/provider/cron/parsed_spec.rb +4 -3
- data/spec/unit/provider/group/ldap_spec.rb +22 -25
- data/spec/unit/provider/group/pw_spec.rb +7 -10
- data/spec/unit/provider/host/parsed_spec.rb +3 -17
- data/spec/unit/provider/nameservice/directoryservice_spec.rb +97 -103
- data/spec/unit/provider/package/aix_spec.rb +5 -8
- data/spec/unit/provider/package/apt_spec.rb +3 -6
- data/spec/unit/provider/package/dnf_spec.rb +29 -31
- data/spec/unit/provider/package/dpkg_spec.rb +18 -21
- data/spec/unit/provider/package/freebsd_spec.rb +4 -7
- data/spec/unit/provider/package/gem_spec.rb +41 -41
- data/spec/unit/provider/package/hpux_spec.rb +7 -10
- data/spec/unit/provider/package/macports_spec.rb +13 -15
- data/spec/unit/provider/package/nim_spec.rb +3 -10
- data/spec/unit/provider/package/openbsd_spec.rb +14 -17
- data/spec/unit/provider/package/pip3_spec.rb +3 -6
- data/spec/unit/provider/package/pip_spec.rb +44 -72
- data/spec/unit/provider/package/pkgin_spec.rb +13 -18
- data/spec/unit/provider/package/pkgng_spec.rb +21 -24
- data/spec/unit/provider/package/puppet_gem_spec.rb +6 -9
- data/spec/unit/provider/package/tdnf_spec.rb +9 -12
- data/spec/unit/provider/package/yum_spec.rb +29 -15
- data/spec/unit/provider/package/zypper_spec.rb +17 -19
- data/spec/unit/provider/service/bsd_spec.rb +8 -12
- data/spec/unit/provider/service/daemontools_spec.rb +12 -20
- data/spec/unit/provider/service/debian_spec.rb +8 -16
- data/spec/unit/provider/service/freebsd_spec.rb +2 -5
- data/spec/unit/provider/service/openbsd_spec.rb +12 -18
- data/spec/unit/provider/service/rcng_spec.rb +3 -7
- data/spec/unit/provider/service/redhat_spec.rb +21 -23
- data/spec/unit/provider/service/runit_spec.rb +9 -19
- data/spec/unit/provider/service/smf_spec.rb +82 -21
- data/spec/unit/provider/service/src_spec.rb +14 -23
- data/spec/unit/provider/user/hpux_spec.rb +2 -5
- data/spec/unit/provider/user/ldap_spec.rb +29 -32
- data/spec/unit/provider/user/pw_spec.rb +10 -13
- data/spec/unit/ssl/key_spec.rb +2 -4
- data/spec/unit/transaction/event_manager_spec.rb +12 -1
- data/spec/unit/transaction/resource_harness_spec.rb +18 -0
- data/spec/unit/transaction_spec.rb +25 -0
- data/spec/unit/util/filetype_spec.rb +13 -5
- data/spec/unit/util/logging_spec.rb +0 -41
- data/spec/unit/util/monkey_patches_spec.rb +18 -5
- data/spec/unit/util/selinux_spec.rb +4 -0
- metadata +6 -2
@@ -49,6 +49,52 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
# Returns the service's FMRI. We fail if multiple FMRIs correspond to
|
53
|
+
# @resource[:name].
|
54
|
+
#
|
55
|
+
# If the service does not exist or we fail to get any FMRIs from svcs,
|
56
|
+
# this method will raise a Puppet::Error
|
57
|
+
def service_fmri
|
58
|
+
return @fmri if @fmri
|
59
|
+
|
60
|
+
# `svcs -l` is better to use because we can detect service instances
|
61
|
+
# that have not yet been activated or enabled (i.e. it lets us detect
|
62
|
+
# services that svcadm has not yet touched). `svcs -H -o fmri` is a bit
|
63
|
+
# more limited.
|
64
|
+
lines = svcs("-l", @resource[:name]).chomp.lines.to_a
|
65
|
+
lines.select! { |line| line =~ /^fmri/ }
|
66
|
+
fmris = lines.map! { |line| line.split(' ')[-1].chomp }
|
67
|
+
unless fmris.length == 1
|
68
|
+
raise Puppet::Error, _("Failed to get the FMRI of the %{service} service: The pattern '%{service}' matches multiple FMRIs! These are the FMRIs it matches: %{all_fmris}") % { service: @resource[:name], all_fmris: fmris.join(', ') }
|
69
|
+
end
|
70
|
+
|
71
|
+
@fmri = fmris.first
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true if the provider supports incomplete services.
|
75
|
+
def supports_incomplete_services?
|
76
|
+
Puppet::Util::Package.versioncmp(Facter.value(:operatingsystemrelease), '11.1') >= 0
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns true if the service is complete. A complete service is a service that
|
80
|
+
# has the general/complete property defined.
|
81
|
+
def complete_service?
|
82
|
+
unless supports_incomplete_services?
|
83
|
+
raise Puppet::Error, _("Cannot query if the %{service} service is complete: The concept of complete/incomplete services was introduced in Solaris 11.1. You are on a Solaris %{release} machine.") % { service: @resource[:name], release: Facter.value(:operatingsystemrelease) }
|
84
|
+
end
|
85
|
+
|
86
|
+
return @complete_service if @complete_service
|
87
|
+
|
88
|
+
# We need to use the service's FMRI when querying its config. because
|
89
|
+
# general/complete is an instance-specific property.
|
90
|
+
fmri = service_fmri
|
91
|
+
|
92
|
+
# Check if the general/complete property is defined. If it is undefined,
|
93
|
+
# then svccfg will not print anything to the console.
|
94
|
+
property_defn = svccfg("-s", fmri, "listprop", "general/complete").chomp
|
95
|
+
@complete_service = ! property_defn.empty?
|
96
|
+
end
|
97
|
+
|
52
98
|
def enable
|
53
99
|
self.start
|
54
100
|
end
|
@@ -131,6 +177,14 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
|
|
131
177
|
end
|
132
178
|
|
133
179
|
begin
|
180
|
+
if supports_incomplete_services?
|
181
|
+
unless complete_service?
|
182
|
+
debug _("The %{service} service is incomplete so its status will be reported as :stopped. See `svcs -xv %{fmri}` for more details.") % { service: @resource[:name], fmri: service_fmri }
|
183
|
+
|
184
|
+
return :stopped
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
134
188
|
# get the current state and the next state, and if the next
|
135
189
|
# state is set (i.e. not "-") use it for state comparison
|
136
190
|
states = service_states
|
data/lib/puppet/transaction.rb
CHANGED
@@ -56,6 +56,8 @@ class Puppet::Transaction
|
|
56
56
|
@prefetched_providers = Hash.new { |h,k| h[k] = {} }
|
57
57
|
|
58
58
|
@prefetch_failed_providers = Hash.new { |h,k| h[k] = {} }
|
59
|
+
|
60
|
+
@failed_dependencies_already_notified = Set.new()
|
59
61
|
end
|
60
62
|
|
61
63
|
# Invoke the pre_run_check hook in every resource in the catalog.
|
@@ -291,8 +293,9 @@ class Puppet::Transaction
|
|
291
293
|
if s && s.dependency_failed?
|
292
294
|
# See above. --daniel 2011-06-06
|
293
295
|
unless suppress_report then
|
294
|
-
s.failed_dependencies.each do |dep|
|
296
|
+
s.failed_dependencies.find_all { |d| !(@failed_dependencies_already_notified.include?(d.ref)) }.each do |dep|
|
295
297
|
resource.notice _("Dependency %{dep} has failures: %{status}") % { dep: dep, status: resource_status(dep).failed }
|
298
|
+
@failed_dependencies_already_notified.add(dep.ref)
|
296
299
|
end
|
297
300
|
end
|
298
301
|
end
|
@@ -147,12 +147,18 @@ class Puppet::Transaction::EventManager
|
|
147
147
|
resource.send(callback)
|
148
148
|
|
149
149
|
if not resource.is_a?(Puppet::Type.type(:whit))
|
150
|
-
|
150
|
+
message = n_("Triggered '%{callback}' from %{count} event", "Triggered '%{callback}' from %{count} events", events.length) % { count: events.length, callback: callback }
|
151
|
+
resource.notice message
|
152
|
+
add_callback_status_event(resource, callback, message, "success")
|
151
153
|
end
|
154
|
+
|
152
155
|
return true
|
153
156
|
rescue => detail
|
154
157
|
resource_error_message = _("Failed to call %{callback}: %{detail}") % { callback: callback, detail: detail }
|
155
158
|
resource.err resource_error_message
|
159
|
+
if not resource.is_a?(Puppet::Type.type(:whit))
|
160
|
+
add_callback_status_event(resource, callback, resource_error_message, "failure")
|
161
|
+
end
|
156
162
|
|
157
163
|
transaction.resource_status(resource).failed_to_restart = true
|
158
164
|
transaction.resource_status(resource).fail_with_event(resource_error_message)
|
@@ -160,6 +166,12 @@ class Puppet::Transaction::EventManager
|
|
160
166
|
return false
|
161
167
|
end
|
162
168
|
|
169
|
+
def add_callback_status_event(resource, callback, message, status)
|
170
|
+
options = { message: message, status: status, name: callback.to_s }
|
171
|
+
event = resource.event options
|
172
|
+
transaction.resource_status(resource) << event if event
|
173
|
+
end
|
174
|
+
|
163
175
|
def process_noop_events(resource, callback, events)
|
164
176
|
resource.notice n_("Would have triggered '%{callback}' from %{count} event", "Would have triggered '%{callback}' from %{count} events", events.length) % { count: events.length, callback: callback }
|
165
177
|
|
@@ -158,7 +158,9 @@ class Puppet::Transaction::ResourceHarness
|
|
158
158
|
raise
|
159
159
|
ensure
|
160
160
|
if event
|
161
|
-
|
161
|
+
name = param.name.to_s
|
162
|
+
event.message ||= _("could not create change error message for %{name}") % { name: name }
|
163
|
+
event.calculate_corrective_change(@persistence.get_system_value(context.resource.ref, name))
|
162
164
|
context.record(event)
|
163
165
|
event.send_log
|
164
166
|
context.synced_params << param.name
|
@@ -106,7 +106,7 @@ module Puppet
|
|
106
106
|
def run
|
107
107
|
# For most applications, we want to be able to load code from the modulepath,
|
108
108
|
# such as apply, describe, resource, and faces.
|
109
|
-
# For agent, we only want to load pluginsync'ed code from libdir.
|
109
|
+
# For agent and device in agent mode, we only want to load pluginsync'ed code from libdir.
|
110
110
|
# For master, we shouldn't ever be loading per-environment code into the master's
|
111
111
|
# ruby process, but that requires fixing (#17210, #12173, #8750). So for now
|
112
112
|
# we try to restrict to only code that can be autoloaded from the node's
|
@@ -116,8 +116,7 @@ module Puppet
|
|
116
116
|
# have an appropriate application-wide current_environment set.
|
117
117
|
# If we cannot find the configured environment, which may not exist,
|
118
118
|
# we do not attempt to add plugin directories to the load path.
|
119
|
-
|
120
|
-
if @subcommand_name != 'master' and @subcommand_name != 'agent'
|
119
|
+
unless @subcommand_name == 'master' || @subcommand_name == 'agent' || (@subcommand_name == 'device' && (['--apply', '--facts', '--resource'] - @command_line.args).empty?)
|
121
120
|
if configured_environment = Puppet.lookup(:environments).get(Puppet[:environment])
|
122
121
|
configured_environment.each_plugin_directory do |dir|
|
123
122
|
$LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
data/lib/puppet/util/filetype.rb
CHANGED
@@ -191,13 +191,21 @@ class Puppet::Util::FileType
|
|
191
191
|
|
192
192
|
# Read a specific @path's cron tab.
|
193
193
|
def read
|
194
|
+
unless Puppet::Util.uid(@path)
|
195
|
+
Puppet.debug _("The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.") % { path: @path }
|
196
|
+
|
197
|
+
return ""
|
198
|
+
end
|
199
|
+
|
194
200
|
Puppet::Util::Execution.execute("#{cmdbase} -l", failonfail: true, combine: true)
|
195
201
|
rescue => detail
|
196
202
|
case detail.to_s
|
197
203
|
when /no crontab for/
|
198
204
|
return ""
|
199
205
|
when /are not allowed to/
|
200
|
-
|
206
|
+
Puppet.debug _("The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).") % { path: @path }
|
207
|
+
|
208
|
+
return ""
|
201
209
|
else
|
202
210
|
raise FileReadError, _("Could not read crontab for %{path}: %{detail}") % { path: @path, detail: detail }, detail.backtrace
|
203
211
|
end
|
@@ -215,7 +223,15 @@ class Puppet::Util::FileType
|
|
215
223
|
|
216
224
|
# Overwrite a specific @path's cron tab; must be passed the @path name
|
217
225
|
# and the text with which to create the cron tab.
|
226
|
+
#
|
227
|
+
# TODO: We should refactor this at some point to make it identical to the
|
228
|
+
# :aixtab and :suntab's write methods so that, at the very least, the pipe
|
229
|
+
# is not created and the crontab command's errors are not swallowed.
|
218
230
|
def write(text)
|
231
|
+
unless Puppet::Util.uid(@path)
|
232
|
+
raise Puppet::Error, _("Cannot write the %{path} user's crontab: The user does not exist") % { path: @path }
|
233
|
+
end
|
234
|
+
|
219
235
|
# this file is managed by the OS and should be using system encoding
|
220
236
|
IO.popen("#{cmdbase()} -", "w", :encoding => Encoding.default_external) { |p|
|
221
237
|
p.print text
|
@@ -240,13 +256,21 @@ class Puppet::Util::FileType
|
|
240
256
|
newfiletype(:suntab) do
|
241
257
|
# Read a specific @path's cron tab.
|
242
258
|
def read
|
259
|
+
unless Puppet::Util.uid(@path)
|
260
|
+
Puppet.debug _("The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.") % { path: @path }
|
261
|
+
|
262
|
+
return ""
|
263
|
+
end
|
264
|
+
|
243
265
|
Puppet::Util::Execution.execute(%w{crontab -l}, cronargs)
|
244
266
|
rescue => detail
|
245
267
|
case detail.to_s
|
246
268
|
when /can't open your crontab/
|
247
269
|
return ""
|
248
270
|
when /you are not authorized to use cron/
|
249
|
-
|
271
|
+
Puppet.debug _("The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).") % { path: @path }
|
272
|
+
|
273
|
+
return ""
|
250
274
|
else
|
251
275
|
raise FileReadError, _("Could not read crontab for %{path}: %{detail}") % { path: @path, detail: detail }, detail.backtrace
|
252
276
|
end
|
@@ -283,13 +307,21 @@ class Puppet::Util::FileType
|
|
283
307
|
newfiletype(:aixtab) do
|
284
308
|
# Read a specific @path's cron tab.
|
285
309
|
def read
|
310
|
+
unless Puppet::Util.uid(@path)
|
311
|
+
Puppet.debug _("The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.") % { path: @path }
|
312
|
+
|
313
|
+
return ""
|
314
|
+
end
|
315
|
+
|
286
316
|
Puppet::Util::Execution.execute(%w{crontab -l}, cronargs)
|
287
317
|
rescue => detail
|
288
318
|
case detail.to_s
|
289
319
|
when /open.*in.*directory/
|
290
320
|
return ""
|
291
|
-
when /
|
292
|
-
|
321
|
+
when /not.*authorized.*cron/
|
322
|
+
Puppet.debug _("The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).") % { path: @path }
|
323
|
+
|
324
|
+
return ""
|
293
325
|
else
|
294
326
|
raise FileReadError, _("Could not read crontab for %{path}: %{detail}") % { path: @path, detail: detail }, detail.backtrace
|
295
327
|
end
|
data/lib/puppet/util/selinux.rb
CHANGED
@@ -190,7 +190,7 @@ module Puppet::Util::SELinux
|
|
190
190
|
def selinux_label_support?(file)
|
191
191
|
fstype = find_fs(file)
|
192
192
|
return false if fstype.nil?
|
193
|
-
filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs']
|
193
|
+
filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs', 'tmpfs']
|
194
194
|
filesystems.include?(fstype)
|
195
195
|
end
|
196
196
|
|
@@ -58,7 +58,7 @@ module Puppet::Util::Windows::APITypes
|
|
58
58
|
str = get_bytes(0, char_length * 2).force_encoding('UTF-16LE')
|
59
59
|
str.encode(dst_encoding, str.encoding, encode_options)
|
60
60
|
rescue Exception => e
|
61
|
-
Puppet.debug "Unable to convert value #{str.dump} to encoding #{dst_encoding} due to #{e.inspect}"
|
61
|
+
Puppet.debug "Unable to convert value #{str.nil? ? 'nil' : str.dump} to encoding #{dst_encoding} due to #{e.inspect}"
|
62
62
|
raise
|
63
63
|
end
|
64
64
|
|
@@ -65,6 +65,28 @@ module Puppet::Util::Windows
|
|
65
65
|
vals
|
66
66
|
end
|
67
67
|
|
68
|
+
# Retrieve a set of values from a registry key given their names
|
69
|
+
# Value names listed but not found in the registry will not be added to the
|
70
|
+
# resultant Hashtable
|
71
|
+
#
|
72
|
+
# @param key [RegistryKey] An open handle to a Registry Key
|
73
|
+
# @param names [String[]] An array of names of registry values to return if they exist
|
74
|
+
# @return [Hashtable<String, Object>] A hashtable of all of the found values in the registry key
|
75
|
+
def values_by_name(key, names)
|
76
|
+
vals = {}
|
77
|
+
names.each do |name|
|
78
|
+
FFI::Pointer.from_string_to_wide_string(name) do |subkeyname_ptr|
|
79
|
+
begin
|
80
|
+
_, vals[name] = read(key, subkeyname_ptr)
|
81
|
+
rescue Puppet::Util::Windows::Error => e
|
82
|
+
# ignore missing names, but raise other errors
|
83
|
+
raise e unless e.code == Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
vals
|
88
|
+
end
|
89
|
+
|
68
90
|
def each_value(key, &block)
|
69
91
|
index = 0
|
70
92
|
subkey = nil
|
@@ -104,7 +126,7 @@ module Puppet::Util::Windows
|
|
104
126
|
|
105
127
|
if result != FFI::ERROR_SUCCESS
|
106
128
|
msg = _("Failed to enumerate %{key} registry keys at index %{index}") % { key: key.keyname, index: index }
|
107
|
-
raise Puppet::Util::Windows::Error.new(msg)
|
129
|
+
raise Puppet::Util::Windows::Error.new(msg, result)
|
108
130
|
end
|
109
131
|
|
110
132
|
filetime = FFI::WIN32::FILETIME.new(filetime_ptr)
|
@@ -135,7 +157,7 @@ module Puppet::Util::Windows
|
|
135
157
|
|
136
158
|
if result != FFI::ERROR_SUCCESS
|
137
159
|
msg = _("Failed to enumerate %{key} registry values at index %{index}") % { key: key.keyname, index: index }
|
138
|
-
raise Puppet::Util::Windows::Error.new(msg)
|
160
|
+
raise Puppet::Util::Windows::Error.new(msg, result)
|
139
161
|
end
|
140
162
|
|
141
163
|
subkey_length = subkey_length_ptr.read_dword
|
@@ -165,7 +187,7 @@ module Puppet::Util::Windows
|
|
165
187
|
|
166
188
|
if status != FFI::ERROR_SUCCESS
|
167
189
|
msg = _("Failed to query registry %{key} for sizes") % { key: key.keyname }
|
168
|
-
raise Puppet::Util::Windows::Error.new(msg)
|
190
|
+
raise Puppet::Util::Windows::Error.new(msg, status)
|
169
191
|
end
|
170
192
|
|
171
193
|
result = [
|
@@ -247,8 +269,10 @@ module Puppet::Util::Windows
|
|
247
269
|
buffer_ptr, length_ptr)
|
248
270
|
|
249
271
|
if result != FFI::ERROR_SUCCESS
|
250
|
-
|
251
|
-
|
272
|
+
# buffer is raw bytes, *not* chars - less a NULL terminator
|
273
|
+
name_length = (name_ptr.size / FFI.type_size(:wchar)) - 1 if name_ptr.size > 0
|
274
|
+
msg = _("Failed to read registry value %{value} at %{key}") % { value: name_ptr.read_wide_string(name_length), key: key.keyname }
|
275
|
+
raise Puppet::Util::Windows::Error.new(msg, result)
|
252
276
|
end
|
253
277
|
|
254
278
|
# allows caller to use FFI MemoryPointer helpers to read / shape
|
@@ -360,13 +360,14 @@ module Puppet::Util::Windows
|
|
360
360
|
# @param [:string] service_name name of the service to query
|
361
361
|
# @return [string] the status of the service
|
362
362
|
def service_state(service_name)
|
363
|
-
|
363
|
+
state = nil
|
364
364
|
open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS) do |service|
|
365
|
-
|
365
|
+
query_status(service) do |status|
|
366
|
+
state = SERVICE_STATES[status[:dwCurrentState]]
|
367
|
+
end
|
366
368
|
end
|
367
|
-
state = SERVICE_STATES[status[:dwCurrentState]]
|
368
369
|
if state.nil?
|
369
|
-
raise Puppet::Error.new(_("Unknown Service state '%{current_state}' for '%{service_name}'") % { current_state:
|
370
|
+
raise Puppet::Error.new(_("Unknown Service state '%{current_state}' for '%{service_name}'") % { current_state: state.to_s, service_name: service_name})
|
370
371
|
end
|
371
372
|
state
|
372
373
|
end
|
@@ -377,13 +378,14 @@ module Puppet::Util::Windows
|
|
377
378
|
# @param [:string] service_name name of the service to query
|
378
379
|
# @return [QUERY_SERVICE_CONFIGW.struct] the configuration of the service
|
379
380
|
def service_start_type(service_name)
|
380
|
-
|
381
|
+
start_type = nil
|
381
382
|
open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service|
|
382
|
-
|
383
|
+
query_config(service) do |config|
|
384
|
+
start_type = SERVICE_START_TYPES[config[:dwStartType]]
|
385
|
+
end
|
383
386
|
end
|
384
|
-
start_type = SERVICE_START_TYPES[config[:dwStartType]]
|
385
387
|
if start_type.nil?
|
386
|
-
raise Puppet::Error.new(_("Unknown start type '%{start_type}' for '%{service_name}'") % { start_type:
|
388
|
+
raise Puppet::Error.new(_("Unknown start type '%{start_type}' for '%{service_name}'") % { start_type: start_type.to_s, service_name: service_name})
|
387
389
|
end
|
388
390
|
start_type
|
389
391
|
end
|
@@ -562,62 +564,62 @@ module Puppet::Util::Windows
|
|
562
564
|
def transition_service_state(service_name, valid_initial_states, final_state, &block)
|
563
565
|
service_access = SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS
|
564
566
|
open_service(service_name, SC_MANAGER_CONNECT, service_access) do |service|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
end
|
567
|
+
query_status(service) do |status|
|
568
|
+
initial_state = status[:dwCurrentState]
|
569
|
+
# If the service is already in the final_state, then
|
570
|
+
# no further work needs to be done
|
571
|
+
if initial_state == final_state
|
572
|
+
Puppet.debug _("The service is already in the %{final_state} state. No further work needs to be done.") % { final_state: SERVICE_STATES[final_state] }
|
573
|
+
|
574
|
+
next
|
575
|
+
end
|
575
576
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
577
|
+
# Check that initial_state corresponds to a valid
|
578
|
+
# initial state
|
579
|
+
unless valid_initial_states.include?(initial_state)
|
580
|
+
valid_initial_states_str = valid_initial_states.map do |state|
|
581
|
+
SERVICE_STATES[state]
|
582
|
+
end.join(", ")
|
582
583
|
|
583
|
-
|
584
|
-
|
584
|
+
raise Puppet::Error, _("The service must be in one of the %{valid_initial_states} states to perform this transition. It is currently in the %{current_state} state.") % { valid_initial_states: valid_initial_states_str, current_state: SERVICE_STATES[initial_state] }
|
585
|
+
end
|
585
586
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
587
|
+
# Check if there's a pending transition to the final_state. If so, then wait for
|
588
|
+
# that transition to finish.
|
589
|
+
possible_pending_states = FINAL_STATES.keys.select do |pending_state|
|
590
|
+
# SERVICE_RUNNING has two pending states, SERVICE_START_PENDING and
|
591
|
+
# SERVICE_CONTINUE_PENDING. That is why we need the #select here
|
592
|
+
FINAL_STATES[pending_state] == final_state
|
593
|
+
end
|
594
|
+
if possible_pending_states.include?(initial_state)
|
595
|
+
Puppet.debug _("There is already a pending transition to the %{final_state} state for the %{service_name} service.") % { final_state: SERVICE_STATES[final_state], service_name: service_name }
|
596
|
+
wait_on_pending_state(service, initial_state)
|
596
597
|
|
597
|
-
|
598
|
-
|
598
|
+
next
|
599
|
+
end
|
599
600
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
601
|
+
# If we are in an unsafe pending state like SERVICE_START_PENDING
|
602
|
+
# or SERVICE_STOP_PENDING, then we want to wait for that pending
|
603
|
+
# transition to finish before transitioning the service state.
|
604
|
+
# The reason we do this is because SERVICE_START_PENDING is when
|
605
|
+
# the service thread is being created and initialized, while
|
606
|
+
# SERVICE_STOP_PENDING is when the service thread is being cleaned
|
607
|
+
# up and destroyed. Thus there is a chance that when the service is
|
608
|
+
# in either of these states, its service thread may not yet be ready
|
609
|
+
# to perform the state transition (it may not even exist).
|
610
|
+
if UNSAFE_PENDING_STATES.include?(initial_state)
|
611
|
+
Puppet.debug _("The service is in the %{pending_state} state, which is an unsafe pending state.") % { pending_state: SERVICE_STATES[initial_state] }
|
612
|
+
wait_on_pending_state(service, initial_state)
|
613
|
+
initial_state = FINAL_STATES[initial_state]
|
614
|
+
end
|
614
615
|
|
615
|
-
|
616
|
+
Puppet.debug _("Transitioning the %{service_name} service from %{initial_state} to %{final_state}") % { service_name: service_name, initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state] }
|
616
617
|
|
617
|
-
|
618
|
+
yield service
|
618
619
|
|
619
|
-
|
620
|
-
|
620
|
+
Puppet.debug _("Waiting for the transition to finish")
|
621
|
+
wait_on_state_transition(service, initial_state, final_state)
|
622
|
+
end
|
621
623
|
end
|
622
624
|
rescue => detail
|
623
625
|
raise Puppet::Error, _("Failed to transition the %{service_name} service to the %{final_state} state. Detail: %{detail}") % { service_name: service_name, final_state: SERVICE_STATES[final_state], detail: detail }, detail.backtrace
|
@@ -661,9 +663,9 @@ module Puppet::Util::Windows
|
|
661
663
|
if success == FFI::WIN32_FALSE
|
662
664
|
raise Puppet::Util::Windows::Error.new(_("Service query failed"))
|
663
665
|
end
|
666
|
+
yield status
|
664
667
|
end
|
665
668
|
end
|
666
|
-
status
|
667
669
|
end
|
668
670
|
private :query_status
|
669
671
|
|
@@ -673,7 +675,7 @@ module Puppet::Util::Windows
|
|
673
675
|
#
|
674
676
|
# @param [:handle] service handle of the service to query
|
675
677
|
# @return [QUERY_SERVICE_CONFIGW struct] the result of the query
|
676
|
-
def query_config(service)
|
678
|
+
def query_config(service, &block)
|
677
679
|
config = nil
|
678
680
|
size_required = nil
|
679
681
|
# Fetch the bytes of memory required to be allocated
|
@@ -697,9 +699,9 @@ module Puppet::Util::Windows
|
|
697
699
|
if success == FFI::WIN32_FALSE
|
698
700
|
raise Puppet::Util::Windows::Error.new(_("Service query failed"))
|
699
701
|
end
|
702
|
+
yield config
|
700
703
|
end
|
701
704
|
end
|
702
|
-
config
|
703
705
|
end
|
704
706
|
private :query_config
|
705
707
|
|
@@ -712,7 +714,7 @@ module Puppet::Util::Windows
|
|
712
714
|
FFI::MemoryPointer.new(SERVICE_STATUS.size) do |status_ptr|
|
713
715
|
status = SERVICE_STATUS.new(status_ptr)
|
714
716
|
if ControlService(service, signal, status) == FFI::WIN32_FALSE
|
715
|
-
raise Puppet::Util::Windows::Error, _("Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}.
|
717
|
+
raise Puppet::Util::Windows::Error, _("Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Reason for failure:") % { control_signal: SERVICE_CONTROL_SIGNALS[signal], current_state: SERVICE_STATES[status[:dwCurrentState]] }
|
716
718
|
end
|
717
719
|
end
|
718
720
|
end
|
@@ -741,21 +743,25 @@ module Puppet::Util::Windows
|
|
741
743
|
state = nil
|
742
744
|
elapsed_time = 0
|
743
745
|
while elapsed_time <= DEFAULT_TIMEOUT
|
744
|
-
status = query_status(service)
|
745
|
-
state = status[:dwCurrentState]
|
746
|
-
|
747
|
-
return if state == final_state
|
748
|
-
if state == pending_state
|
749
|
-
Puppet.debug _("The service transitioned to the %{pending_state} state.") % { pending_state: SERVICE_STATES[pending_state] }
|
750
|
-
wait_on_pending_state(service, pending_state)
|
751
|
-
return
|
752
|
-
end
|
753
746
|
|
754
|
-
|
755
|
-
|
747
|
+
query_status(service) do |status|
|
748
|
+
state = status[:dwCurrentState]
|
749
|
+
return if state == final_state
|
750
|
+
if state == pending_state
|
751
|
+
Puppet.debug _("The service transitioned to the %{pending_state} state.") % { pending_state: SERVICE_STATES[pending_state] }
|
752
|
+
wait_on_pending_state(service, pending_state)
|
753
|
+
return
|
754
|
+
end
|
755
|
+
sleep(1)
|
756
|
+
elapsed_time += 1
|
757
|
+
end
|
756
758
|
end
|
757
|
-
|
758
759
|
# Timed out while waiting for the transition to finish. Raise an error
|
760
|
+
# We can still use the state variable read from the FFI struct because
|
761
|
+
# FFI creates new Integer objects during an assignment of an integer value
|
762
|
+
# stored in an FFI struct. We verified that the '=' operater is safe
|
763
|
+
# from the freed memory since the new ruby object created during the
|
764
|
+
# assignment will remain in ruby memory and remain immutable and constant.
|
759
765
|
raise Puppet::Error, _("Timed out while waiting for the service to transition from %{initial_state} to %{final_state} OR from %{initial_state} to %{pending_state} to %{final_state}. The service's current state is %{current_state}.") % { initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state], pending_state: SERVICE_STATES[pending_state], current_state: SERVICE_STATES[state] }
|
760
766
|
end
|
761
767
|
private :wait_on_state_transition
|
@@ -775,36 +781,37 @@ module Puppet::Util::Windows
|
|
775
781
|
elapsed_time = 0
|
776
782
|
last_checkpoint = -1
|
777
783
|
loop do
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
784
|
+
query_status(service) do |status|
|
785
|
+
state = status[:dwCurrentState]
|
786
|
+
checkpoint = status[:dwCheckPoint]
|
787
|
+
wait_hint = status[:dwWaitHint]
|
788
|
+
# Check if our service has finished transitioning to
|
789
|
+
# the final_state OR if an unexpected transition
|
790
|
+
# has occurred
|
791
|
+
return if state == final_state
|
792
|
+
unless state == pending_state
|
793
|
+
raise Puppet::Error, _("Unexpected transition to the %{current_state} state while waiting for the pending transition from %{pending_state} to %{final_state} to finish.") % { current_state: SERVICE_STATES[state], pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state] }
|
794
|
+
end
|
788
795
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
796
|
+
# Check if any progress has been made since our last sleep
|
797
|
+
# using the dwCheckPoint. If no progress has been made then
|
798
|
+
# check if we've timed out, and raise an error if so
|
799
|
+
if checkpoint > last_checkpoint
|
800
|
+
elapsed_time = 0
|
801
|
+
last_checkpoint = checkpoint
|
802
|
+
else
|
803
|
+
wait_hint_in_seconds = milliseconds_to_seconds(wait_hint)
|
804
|
+
timeout = wait_hint_in_seconds < DEFAULT_TIMEOUT ? DEFAULT_TIMEOUT : wait_hint_in_seconds
|
805
|
+
|
806
|
+
if elapsed_time >= timeout
|
807
|
+
raise Puppet::Error, _("Timed out while waiting for the pending transition from %{pending_state} to %{final_state} to finish. The current state is %{current_state}.") % { pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state], current_state: SERVICE_STATES[state] }
|
808
|
+
end
|
801
809
|
end
|
810
|
+
wait_time = wait_hint_to_wait_time(wait_hint)
|
811
|
+
# Wait a bit before rechecking the service's state
|
812
|
+
sleep(wait_time)
|
813
|
+
elapsed_time += wait_time
|
802
814
|
end
|
803
|
-
|
804
|
-
# Wait a bit before rechecking the service's state
|
805
|
-
wait_time = wait_hint_to_wait_time(status[:dwWaitHint])
|
806
|
-
sleep(wait_time)
|
807
|
-
elapsed_time += wait_time
|
808
815
|
end
|
809
816
|
end
|
810
817
|
private :wait_on_pending_state
|