puppet 5.5.6 → 5.5.7
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 +3 -1
- data/Gemfile.lock +12 -12
- data/Rakefile +9 -0
- data/lib/puppet/application.rb +5 -0
- data/lib/puppet/application/apply.rb +1 -0
- data/lib/puppet/application/master.rb +9 -7
- data/lib/puppet/application/script.rb +1 -1
- data/lib/puppet/defaults.rb +51 -31
- data/lib/puppet/etc.rb +20 -0
- data/lib/puppet/file_serving/fileset.rb +1 -1
- data/lib/puppet/functions.rb +123 -0
- data/lib/puppet/functions/new.rb +37 -53
- data/lib/puppet/functions/warning.rb +1 -1
- data/lib/puppet/loaders.rb +1 -0
- data/lib/puppet/parser/functions.rb +3 -1
- data/lib/puppet/parser/functions/sprintf.rb +12 -1
- data/lib/puppet/pops/evaluator/runtime3_converter.rb +16 -0
- data/lib/puppet/pops/evaluator/runtime3_support.rb +3 -4
- data/lib/puppet/pops/issues.rb +8 -0
- data/lib/puppet/pops/loader/loader.rb +2 -2
- data/lib/puppet/pops/loader/loader_paths.rb +3 -1
- data/lib/puppet/pops/loader/module_loaders.rb +1 -1
- data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +62 -0
- data/lib/puppet/pops/loaders.rb +5 -21
- data/lib/puppet/pops/parser/heredoc_support.rb +1 -2
- data/lib/puppet/pops/parser/lexer2.rb +1 -1
- data/lib/puppet/pops/validation/checker4_0.rb +31 -6
- data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
- data/lib/puppet/property/keyvalue.rb +70 -8
- data/lib/puppet/provider/aix_object.rb +483 -0
- data/lib/puppet/provider/exec.rb +54 -57
- data/lib/puppet/provider/group/aix.rb +40 -115
- data/lib/puppet/provider/group/pw.rb +4 -8
- data/lib/puppet/provider/group/windows_adsi.rb +7 -4
- data/lib/puppet/provider/nameservice.rb +1 -25
- data/lib/puppet/provider/nameservice/directoryservice.rb +5 -3
- data/lib/puppet/provider/package/portage.rb +2 -2
- data/lib/puppet/provider/package/windows.rb +2 -2
- data/lib/puppet/provider/package/windows/exe_package.rb +3 -10
- data/lib/puppet/provider/package/zypper.rb +1 -1
- data/lib/puppet/provider/service/launchd.rb +19 -3
- data/lib/puppet/provider/service/windows.rb +49 -40
- data/lib/puppet/provider/user/aix.rb +180 -246
- data/lib/puppet/provider/user/windows_adsi.rb +9 -1
- data/lib/puppet/resource/catalog.rb +1 -5
- data/lib/puppet/type/augeas.rb +1 -1
- data/lib/puppet/type/exec.rb +16 -14
- data/lib/puppet/type/file.rb +2 -2
- data/lib/puppet/type/file/source.rb +9 -5
- data/lib/puppet/type/group.rb +65 -23
- data/lib/puppet/type/k5login.rb +2 -2
- data/lib/puppet/type/notify.rb +1 -1
- data/lib/puppet/type/package.rb +3 -6
- data/lib/puppet/type/resources.rb +12 -2
- data/lib/puppet/type/schedule.rb +8 -1
- data/lib/puppet/type/selboolean.rb +2 -2
- data/lib/puppet/type/selmodule.rb +3 -4
- data/lib/puppet/type/service.rb +2 -5
- data/lib/puppet/type/tidy.rb +1 -1
- data/lib/puppet/type/user.rb +15 -20
- data/lib/puppet/type/yumrepo.rb +2 -2
- data/lib/puppet/type/zone.rb +2 -2
- data/lib/puppet/util.rb +7 -3
- data/lib/puppet/util/execution.rb +15 -1
- data/lib/puppet/util/posix.rb +15 -0
- data/lib/puppet/util/storage.rb +12 -0
- data/lib/puppet/util/windows.rb +4 -2
- data/lib/puppet/util/windows/adsi.rb +235 -205
- data/lib/puppet/util/windows/process.rb +23 -3
- data/lib/puppet/util/windows/security.rb +14 -0
- data/lib/puppet/util/windows/service.rb +977 -0
- data/lib/puppet/util/windows/user.rb +3 -5
- data/lib/puppet/version.rb +1 -1
- data/locales/ja/puppet.po +705 -374
- data/locales/puppet.pot +485 -261
- data/man/man5/puppet.conf.5 +36 -15
- 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 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- 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/aix_object/aix_colon_list_real_world_input.out +1 -0
- data/spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_output.out +1 -0
- data/spec/fixtures/unit/provider/user/aix/aix_passwd_file.out +32 -0
- data/spec/integration/parser/collection_spec.rb +4 -8
- data/spec/integration/provider/service/windows_spec.rb +5 -5
- data/spec/integration/type/file_spec.rb +6 -6
- data/spec/integration/util/windows/adsi_spec.rb +6 -5
- data/spec/integration/util/windows/security_spec.rb +10 -7
- data/spec/integration/util/windows/user_spec.rb +37 -17
- data/spec/spec_helper.rb +0 -1
- data/spec/unit/application/apply_spec.rb +41 -2
- data/spec/unit/application/master_spec.rb +7 -0
- data/spec/unit/application_spec.rb +21 -3
- data/spec/unit/defaults_spec.rb +20 -0
- data/spec/unit/etc_spec.rb +25 -0
- data/spec/unit/file_serving/fileset_spec.rb +11 -11
- data/spec/unit/gettext/config_spec.rb +1 -1
- data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +6 -6
- data/spec/unit/pops/loaders/loaders_spec.rb +40 -7
- data/spec/unit/pops/parser/parse_heredoc_spec.rb +16 -0
- data/spec/unit/pops/validator/validator_spec.rb +129 -10
- data/spec/unit/property/keyvalue_spec.rb +97 -6
- data/spec/unit/provider/aix_object_spec.rb +805 -0
- data/spec/unit/provider/group/aix_spec.rb +57 -0
- data/spec/unit/provider/group/pw_spec.rb +0 -6
- data/spec/unit/provider/group/windows_adsi_spec.rb +34 -35
- data/spec/unit/provider/nameservice/directoryservice_spec.rb +2 -2
- data/spec/unit/provider/package/windows/exe_package_spec.rb +3 -3
- data/spec/unit/provider/package/windows_spec.rb +4 -4
- data/spec/unit/provider/service/launchd_spec.rb +19 -0
- data/spec/unit/provider/service/windows_spec.rb +71 -78
- data/spec/unit/provider/user/aix_spec.rb +162 -116
- data/spec/unit/provider/user/windows_adsi_spec.rb +4 -4
- data/spec/unit/resource/catalog_spec.rb +2 -2
- data/spec/unit/ssl/certificate_authority_spec.rb +0 -1
- data/spec/unit/type/group_spec.rb +111 -13
- data/spec/unit/type/resources_spec.rb +18 -0
- data/spec/unit/util/execution_spec.rb +77 -0
- data/spec/unit/util/posix_spec.rb +28 -0
- data/spec/unit/util/storage_spec.rb +107 -0
- data/spec/unit/util/windows/adsi_spec.rb +108 -13
- data/spec/unit/util/windows/service_spec.rb +669 -0
- metadata +17 -5
- data/lib/puppet/provider/aixobject.rb +0 -392
- data/spec/unit/provider/aixobject_spec.rb +0 -101
@@ -0,0 +1,669 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe "Puppet::Util::Windows::Service", :if => Puppet.features.microsoft_windows? do
|
5
|
+
require 'puppet/util/windows'
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Puppet::Util::Windows::Error.stubs(:format_error_code)
|
9
|
+
.with(anything)
|
10
|
+
.returns("fake error!")
|
11
|
+
end
|
12
|
+
|
13
|
+
def service_state_str(state)
|
14
|
+
Puppet::Util::Windows::Service::SERVICE_STATES[state].to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# The following should emulate a successful call to the private function
|
18
|
+
# query_status that returns the value of query_return. This should give
|
19
|
+
# us a way to mock changes in service status.
|
20
|
+
#
|
21
|
+
# Everything else is stubbed, the emulation of the successful call is really
|
22
|
+
# just an expectation of subject::SERVICE_STATUS_PROCESS.new in sequence that
|
23
|
+
# returns the value passed in as a param
|
24
|
+
def expect_successful_status_query_and_return(query_return)
|
25
|
+
subject::SERVICE_STATUS_PROCESS.expects(:new).in_sequence(status_checks).returns(query_return)
|
26
|
+
end
|
27
|
+
|
28
|
+
def expect_successful_status_queries_and_return(*query_returns)
|
29
|
+
query_returns.each do |query_return|
|
30
|
+
expect_successful_status_query_and_return(query_return)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# The following should emulate a successful call to the private function
|
35
|
+
# query_config that returns the value of query_return. This should give
|
36
|
+
# us a way to mock changes in service configuration.
|
37
|
+
#
|
38
|
+
# Everything else is stubbed, the emulation of the successful call is really
|
39
|
+
# just an expectation of subject::QUERY_SERVICE_CONFIGW.new in sequence that
|
40
|
+
# returns the value passed in as a param
|
41
|
+
def expect_successful_config_query_and_return(query_return)
|
42
|
+
subject::QUERY_SERVICE_CONFIGW.expects(:new).in_sequence(status_checks).returns(query_return)
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:subject) { Puppet::Util::Windows::Service }
|
46
|
+
let(:pointer) { mock() }
|
47
|
+
let(:status_checks) { sequence('status_checks') }
|
48
|
+
let(:mock_service_name) { mock() }
|
49
|
+
let(:service) { mock() }
|
50
|
+
let(:scm) { mock() }
|
51
|
+
|
52
|
+
before do
|
53
|
+
subject.stubs(:QueryServiceStatusEx).returns(1)
|
54
|
+
subject.stubs(:QueryServiceConfigW).returns(1)
|
55
|
+
subject.stubs(:ChangeServiceConfigW).returns(1)
|
56
|
+
subject.stubs(:OpenSCManagerW).returns(scm)
|
57
|
+
subject.stubs(:OpenServiceW).returns(service)
|
58
|
+
subject.stubs(:CloseServiceHandle)
|
59
|
+
subject.stubs(:EnumServicesStatusExW).returns(1)
|
60
|
+
subject.stubs(:wide_string)
|
61
|
+
subject::SERVICE_STATUS_PROCESS.stubs(:new)
|
62
|
+
subject::QUERY_SERVICE_CONFIGW.stubs(:new)
|
63
|
+
subject::SERVICE_STATUS.stubs(:new).returns({:dwCurrentState => subject::SERVICE_RUNNING})
|
64
|
+
FFI.stubs(:errno).returns(0)
|
65
|
+
FFI::MemoryPointer.stubs(:new).yields(pointer)
|
66
|
+
pointer.stubs(:read_dword)
|
67
|
+
pointer.stubs(:write_dword)
|
68
|
+
pointer.stubs(:size)
|
69
|
+
subject.stubs(:sleep)
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#exists?" do
|
73
|
+
context "when the service control manager cannot be opened" do
|
74
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
75
|
+
it "raises a puppet error" do
|
76
|
+
expect{ subject.exists?(mock_service_name) }.to raise_error(Puppet::Error)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when the service cannot be opened" do
|
81
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
82
|
+
|
83
|
+
it "returns false if it fails to open because the service does not exist" do
|
84
|
+
FFI.stubs(:errno).returns(Puppet::Util::Windows::Service::ERROR_SERVICE_DOES_NOT_EXIST)
|
85
|
+
|
86
|
+
expect(subject.exists?(mock_service_name)).to be false
|
87
|
+
end
|
88
|
+
|
89
|
+
it "raises a puppet error if it fails to open for some other reason" do
|
90
|
+
expect{ subject.exists?(mock_service_name) }.to raise_error(Puppet::Error)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when the service can be opened" do
|
95
|
+
it "returns true" do
|
96
|
+
expect(subject.exists?(mock_service_name)).to be true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# This shared example contains the unit tests for the wait_on_pending_state
|
102
|
+
# helper as used by service actions like #start and #stop. Before including
|
103
|
+
# this shared example, be sure to mock out any intermediate calls prior to
|
104
|
+
# the pending transition, and make sure that the post-condition _after_ those
|
105
|
+
# intermediate calls leaves the service in the pending state. Before including
|
106
|
+
# this example in your tests, be sure to define the following variables in a `let`
|
107
|
+
# context:
|
108
|
+
# * action -- The service action
|
109
|
+
shared_examples "a service action waiting on a pending transition" do |pending_state|
|
110
|
+
pending_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[pending_state].to_s
|
111
|
+
|
112
|
+
final_state = Puppet::Util::Windows::Service::FINAL_STATES[pending_state]
|
113
|
+
final_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[final_state].to_s
|
114
|
+
|
115
|
+
it "raises a Puppet::Error if the service query fails" do
|
116
|
+
subject.expects(:QueryServiceStatusEx).in_sequence(status_checks).returns(FFI::WIN32_FALSE)
|
117
|
+
|
118
|
+
expect { subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "raises a Puppet::Error if the service unexpectedly transitions to a state other than #{pending_state_str} or #{final_state_str}" do
|
122
|
+
invalid_state = (subject::SERVICE_STATES.keys - [pending_state, final_state]).first
|
123
|
+
|
124
|
+
expect_successful_status_query_and_return(dwCurrentState: invalid_state)
|
125
|
+
|
126
|
+
expect { subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "waits for at least 1 second if the wait_hint/10 is < 1 second" do
|
130
|
+
expect_successful_status_queries_and_return(
|
131
|
+
{ :dwCurrentState => pending_state, :dwWaitHint => 0, :dwCheckPoint => 1 },
|
132
|
+
{ :dwCurrentState => final_state }
|
133
|
+
)
|
134
|
+
|
135
|
+
subject.expects(:sleep).with(1)
|
136
|
+
|
137
|
+
subject.send(action, mock_service_name)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "waits for at most 10 seconds if wait_hint/10 is > 10 seconds" do
|
141
|
+
expect_successful_status_queries_and_return(
|
142
|
+
{ :dwCurrentState => pending_state, :dwWaitHint => 1000000, :dwCheckPoint => 1 },
|
143
|
+
{ :dwCurrentState => final_state }
|
144
|
+
)
|
145
|
+
|
146
|
+
subject.expects(:sleep).with(10)
|
147
|
+
|
148
|
+
subject.send(action, mock_service_name)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "does not raise an error if the service makes any progress while transitioning to #{final_state_str}" do
|
152
|
+
expect_successful_status_queries_and_return(
|
153
|
+
# The three "pending_state" statuses simulate the scenario where the service
|
154
|
+
# makes some progress during the transition right when Puppet's about to
|
155
|
+
# time out.
|
156
|
+
{ :dwCurrentState => pending_state, :dwWaitHint => 100000, :dwCheckPoint => 1 },
|
157
|
+
{ :dwCurrentState => pending_state, :dwWaitHint => 100000, :dwCheckPoint => 1 },
|
158
|
+
{ :dwCurrentState => pending_state, :dwWaitHint => 100000, :dwCheckPoint => 2 },
|
159
|
+
|
160
|
+
{ :dwCurrentState => final_state }
|
161
|
+
)
|
162
|
+
|
163
|
+
expect { subject.send(action, mock_service_name) }.to_not raise_error
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raises a Puppet::Error if it times out while waiting for the transition to #{final_state_str}" do
|
167
|
+
31.times do
|
168
|
+
expect_successful_status_query_and_return(
|
169
|
+
dwCurrentState: pending_state,
|
170
|
+
dwWaitHint: 10000,
|
171
|
+
dwCheckPoint: 1
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
expect { subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# This shared example contains the unit tests for the transition_service_state
|
180
|
+
# helper, which is the helper that all of our service actions like #start, #stop
|
181
|
+
# delegate to. Including these tests under a shared example lets us include them in each of
|
182
|
+
# those service action's unit tests. Before including this example in your tests, be
|
183
|
+
# sure to define the following variables in a `let` context:
|
184
|
+
# * initial_state -- The initial state of the service prior to performing the state
|
185
|
+
# transition
|
186
|
+
#
|
187
|
+
# * mock_state_transition -- A lambda that mocks the state transition. This should mock
|
188
|
+
# any code in the block that's passed to the
|
189
|
+
# transition_service_state helper
|
190
|
+
#
|
191
|
+
# See the unit tests for the #start method to see how this shared example's
|
192
|
+
# included.
|
193
|
+
#
|
194
|
+
shared_examples "a service action that transitions the service state" do |action, valid_initial_states, pending_state, final_state|
|
195
|
+
valid_initial_states_str = valid_initial_states.map do |state|
|
196
|
+
Puppet::Util::Windows::Service::SERVICE_STATES[state]
|
197
|
+
end.join(', ')
|
198
|
+
pending_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[pending_state].to_s
|
199
|
+
final_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[final_state].to_s
|
200
|
+
|
201
|
+
it "noops if the service is already in the #{final_state} state" do
|
202
|
+
expect_successful_status_query_and_return(dwCurrentState: final_state)
|
203
|
+
|
204
|
+
expect { subject.send(action, mock_service_name) }.to_not raise_error
|
205
|
+
end
|
206
|
+
|
207
|
+
# invalid_initial_states will be empty for the #stop action
|
208
|
+
invalid_initial_states = Puppet::Util::Windows::Service::SERVICE_STATES.keys - valid_initial_states - [final_state]
|
209
|
+
unless invalid_initial_states.empty?
|
210
|
+
it "raises a Puppet::Error if the service's initial state is not one of #{valid_initial_states_str}" do
|
211
|
+
invalid_initial_state = invalid_initial_states.first
|
212
|
+
expect_successful_status_query_and_return(dwCurrentState: invalid_initial_state)
|
213
|
+
|
214
|
+
expect{ subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "when there's a pending transition to the #{final_state} state" do
|
219
|
+
before(:each) do
|
220
|
+
expect_successful_status_query_and_return(dwCurrentState: pending_state)
|
221
|
+
end
|
222
|
+
|
223
|
+
include_examples "a service action waiting on a pending transition", pending_state do
|
224
|
+
let(:action) { action }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# If the service action accepts an unsafe pending state as one of the service's
|
229
|
+
# initial states, then we need to test that the action waits for the service to
|
230
|
+
# transition from that unsafe pending state before doing anything else.
|
231
|
+
unsafe_pending_states = valid_initial_states & Puppet::Util::Windows::Service::UNSAFE_PENDING_STATES
|
232
|
+
unless unsafe_pending_states.empty?
|
233
|
+
unsafe_pending_state = unsafe_pending_states.first
|
234
|
+
unsafe_pending_state_str = Puppet::Util::Windows::Service::SERVICE_STATES[unsafe_pending_state]
|
235
|
+
|
236
|
+
context "waiting for a service with #{unsafe_pending_state_str} as its initial state" do
|
237
|
+
before(:each) do
|
238
|
+
# This mocks the status query to return the 'final_state' by default. Otherwise,
|
239
|
+
# we will fail the tests in the latter parts of the code where we wait for the
|
240
|
+
# service to finish transitioning to the 'final_state'.
|
241
|
+
subject::SERVICE_STATUS_PROCESS.stubs(:new).returns(dwCurrentState: final_state)
|
242
|
+
|
243
|
+
# Set our service's initial state
|
244
|
+
expect_successful_status_query_and_return(dwCurrentState: unsafe_pending_state)
|
245
|
+
|
246
|
+
mock_state_transition.call
|
247
|
+
end
|
248
|
+
|
249
|
+
include_examples "a service action waiting on a pending transition", unsafe_pending_state do
|
250
|
+
let(:action) { action }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# reads e.g. "waiting for the service to transition to the SERVICE_RUNNING state after executing the 'start' action"
|
256
|
+
#
|
257
|
+
# NOTE: This is really unit testing the wait_on_state_transition helper
|
258
|
+
context "waiting for the service to transition to the #{final_state_str} state after executing the '#{action}' action" do
|
259
|
+
before(:each) do
|
260
|
+
# Set our service's initial state prior to performing the state transition
|
261
|
+
expect_successful_status_query_and_return(dwCurrentState: initial_state)
|
262
|
+
|
263
|
+
mock_state_transition.call
|
264
|
+
end
|
265
|
+
|
266
|
+
it "raises a Puppet::Error if the service query fails" do
|
267
|
+
subject.expects(:QueryServiceStatusEx).in_sequence(status_checks).returns(FFI::WIN32_FALSE)
|
268
|
+
|
269
|
+
expect { subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
|
270
|
+
end
|
271
|
+
|
272
|
+
it "waits, then queries again until it transitions to #{final_state_str}" do
|
273
|
+
expect_successful_status_queries_and_return(
|
274
|
+
{ :dwCurrentState => initial_state },
|
275
|
+
{ :dwCurrentState => initial_state },
|
276
|
+
{ :dwCurrentState => final_state }
|
277
|
+
)
|
278
|
+
|
279
|
+
subject.expects(:sleep).with(1).twice
|
280
|
+
|
281
|
+
subject.send(action, mock_service_name)
|
282
|
+
end
|
283
|
+
|
284
|
+
context "when it transitions to the #{pending_state_str} state" do
|
285
|
+
before(:each) do
|
286
|
+
expect_successful_status_query_and_return(dwCurrentState: pending_state)
|
287
|
+
end
|
288
|
+
|
289
|
+
include_examples "a service action waiting on a pending transition", pending_state do
|
290
|
+
let(:action) { action }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
it "raises a Puppet::Error if it times out while waiting for the transition to #{final_state_str}" do
|
295
|
+
31.times do
|
296
|
+
expect_successful_status_query_and_return(dwCurrentState: initial_state)
|
297
|
+
end
|
298
|
+
|
299
|
+
expect { subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe "#start" do
|
305
|
+
# rspec will still try to load the tests even though
|
306
|
+
# the :if => Puppet.features.microsoft_windows? filter
|
307
|
+
# is passed-in to the top-level describe block on
|
308
|
+
# non-Windows platforms; it just won't run them. However
|
309
|
+
# on these platforms, the loading will fail because this
|
310
|
+
# test uses a shared example that references variables
|
311
|
+
# from the Windows::Service module when building the unit
|
312
|
+
# tests, which is only available on Windows platforms.
|
313
|
+
# Thus, we add the next here to ensure that rspec does not
|
314
|
+
# attempt to load our test code. This is OK for us to do
|
315
|
+
# because we do not want to run these tests on non-Windows
|
316
|
+
# platforms.
|
317
|
+
next unless Puppet.features.microsoft_windows?
|
318
|
+
|
319
|
+
context "when the service control manager cannot be opened" do
|
320
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
321
|
+
it "raises a puppet error" do
|
322
|
+
expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "when the service cannot be opened" do
|
327
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
328
|
+
it "raises a puppet error" do
|
329
|
+
expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
context "when the service can be opened" do
|
334
|
+
# Can't use rspec's subject here because that
|
335
|
+
# can only be referenced inside an 'it' block.
|
336
|
+
service = Puppet::Util::Windows::Service
|
337
|
+
valid_initial_states = [
|
338
|
+
service::SERVICE_STOP_PENDING,
|
339
|
+
service::SERVICE_STOPPED,
|
340
|
+
service::SERVICE_START_PENDING
|
341
|
+
]
|
342
|
+
final_state = service::SERVICE_RUNNING
|
343
|
+
|
344
|
+
include_examples "a service action that transitions the service state", :start, valid_initial_states, service::SERVICE_START_PENDING, final_state do
|
345
|
+
let(:initial_state) { subject::SERVICE_STOPPED }
|
346
|
+
let(:mock_state_transition) do
|
347
|
+
lambda do
|
348
|
+
subject.stubs(:StartServiceW).returns(1)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
it "raises a Puppet::Error if StartServiceW returns false" do
|
354
|
+
expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_STOPPED)
|
355
|
+
|
356
|
+
subject.expects(:StartServiceW).returns(FFI::WIN32_FALSE)
|
357
|
+
|
358
|
+
expect { subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
359
|
+
end
|
360
|
+
|
361
|
+
it "starts the service" do
|
362
|
+
expect_successful_status_queries_and_return(
|
363
|
+
{ dwCurrentState: subject::SERVICE_STOPPED },
|
364
|
+
{ dwCurrentState: subject::SERVICE_RUNNING }
|
365
|
+
)
|
366
|
+
|
367
|
+
subject.expects(:StartServiceW).returns(1)
|
368
|
+
|
369
|
+
subject.start(mock_service_name)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
describe "#stop" do
|
375
|
+
next unless Puppet.features.microsoft_windows?
|
376
|
+
|
377
|
+
context "when the service control manager cannot be opened" do
|
378
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
379
|
+
it "raises a puppet error" do
|
380
|
+
expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context "when the service cannot be opened" do
|
385
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
386
|
+
it "raises a puppet error" do
|
387
|
+
expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context "when the service can be opened" do
|
392
|
+
service = Puppet::Util::Windows::Service
|
393
|
+
valid_initial_states = service::SERVICE_STATES.keys - [service::SERVICE_STOPPED]
|
394
|
+
final_state = service::SERVICE_STOPPED
|
395
|
+
|
396
|
+
include_examples "a service action that transitions the service state", :stop, valid_initial_states, service::SERVICE_STOP_PENDING, final_state do
|
397
|
+
let(:initial_state) { subject::SERVICE_RUNNING }
|
398
|
+
let(:mock_state_transition) do
|
399
|
+
lambda do
|
400
|
+
subject.stubs(:ControlService).returns(1)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
it "raises a Puppet::Error if ControlService returns false" do
|
406
|
+
expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_RUNNING)
|
407
|
+
|
408
|
+
subject.stubs(:ControlService).returns(FFI::WIN32_FALSE)
|
409
|
+
|
410
|
+
expect { subject.stop(mock_service_name) }.to raise_error(Puppet::Error)
|
411
|
+
end
|
412
|
+
|
413
|
+
it "stops the service" do
|
414
|
+
expect_successful_status_queries_and_return(
|
415
|
+
{ dwCurrentState: subject::SERVICE_RUNNING },
|
416
|
+
{ dwCurrentState: subject::SERVICE_STOPPED }
|
417
|
+
)
|
418
|
+
|
419
|
+
subject.expects(:ControlService).returns(1)
|
420
|
+
|
421
|
+
subject.stop(mock_service_name)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
describe "#resume" do
|
427
|
+
next unless Puppet.features.microsoft_windows?
|
428
|
+
|
429
|
+
context "when the service control manager cannot be opened" do
|
430
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
431
|
+
it "raises a puppet error" do
|
432
|
+
expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
context "when the service cannot be opened" do
|
437
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
438
|
+
it "raises a puppet error" do
|
439
|
+
expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
context "when the service can be opened" do
|
444
|
+
service = Puppet::Util::Windows::Service
|
445
|
+
valid_initial_states = [
|
446
|
+
service::SERVICE_PAUSE_PENDING,
|
447
|
+
service::SERVICE_PAUSED,
|
448
|
+
service::SERVICE_CONTINUE_PENDING
|
449
|
+
]
|
450
|
+
final_state = service::SERVICE_RUNNING
|
451
|
+
|
452
|
+
include_examples "a service action that transitions the service state", :resume, valid_initial_states, service::SERVICE_CONTINUE_PENDING, final_state do
|
453
|
+
let(:initial_state) { service::SERVICE_PAUSED }
|
454
|
+
let(:mock_state_transition) do
|
455
|
+
lambda do
|
456
|
+
# We need to mock the status query because in the block for #resume, we
|
457
|
+
# wait for the service to enter the SERVICE_PAUSED state prior to
|
458
|
+
# performing the transition (in case it is in SERVICE_PAUSE_PENDING).
|
459
|
+
expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSED)
|
460
|
+
|
461
|
+
subject.stubs(:ControlService).returns(1)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
context "waiting for the SERVICE_PAUSE_PENDING => SERVICE_PAUSED transition to finish before resuming it" do
|
467
|
+
before(:each) do
|
468
|
+
# This mocks the status query to return the SERVICE_RUNNING state by default.
|
469
|
+
# Otherwise, we will fail the tests in the latter parts of the code where we
|
470
|
+
# wait for the service to finish transitioning to the 'SERVICE_RUNNING' state.
|
471
|
+
subject::SERVICE_STATUS_PROCESS.stubs(:new).returns(dwCurrentState: subject::SERVICE_RUNNING)
|
472
|
+
|
473
|
+
expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSE_PENDING)
|
474
|
+
|
475
|
+
subject.stubs(:ControlService).returns(1)
|
476
|
+
end
|
477
|
+
|
478
|
+
include_examples "a service action waiting on a pending transition", service::SERVICE_PAUSE_PENDING do
|
479
|
+
let(:action) { :resume }
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
it "raises a Puppet::Error if ControlService returns false" do
|
484
|
+
expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSED)
|
485
|
+
expect_successful_status_query_and_return(dwCurrentState: subject::SERVICE_PAUSED)
|
486
|
+
|
487
|
+
subject.stubs(:ControlService).returns(FFI::WIN32_FALSE)
|
488
|
+
|
489
|
+
expect { subject.resume(mock_service_name) }.to raise_error(Puppet::Error)
|
490
|
+
end
|
491
|
+
|
492
|
+
it "resumes the service" do
|
493
|
+
expect_successful_status_queries_and_return(
|
494
|
+
{ dwCurrentState: subject::SERVICE_PAUSED },
|
495
|
+
{ dwCurrentState: subject::SERVICE_PAUSED },
|
496
|
+
{ dwCurrentState: subject::SERVICE_RUNNING }
|
497
|
+
)
|
498
|
+
|
499
|
+
subject.expects(:ControlService).returns(1)
|
500
|
+
|
501
|
+
subject.resume(mock_service_name)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
describe "#service_state" do
|
507
|
+
context "when the service control manager cannot be opened" do
|
508
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
509
|
+
it "raises a puppet error" do
|
510
|
+
expect{ subject.service_state(mock_service_name) }.to raise_error(Puppet::Error)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
context "when the service cannot be opened" do
|
515
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
516
|
+
it "raises a puppet error" do
|
517
|
+
expect{ subject.service_state(mock_service_name) }.to raise_error(Puppet::Error)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
context "when the service can be opened" do
|
522
|
+
it "raises Puppet::Error if the result of the query is empty" do
|
523
|
+
expect_successful_status_query_and_return({})
|
524
|
+
expect{subject.service_state(mock_service_name)}.to raise_error(Puppet::Error)
|
525
|
+
end
|
526
|
+
|
527
|
+
it "raises Puppet::Error if the result of the query is an unknown state" do
|
528
|
+
expect_successful_status_query_and_return({:dwCurrentState => 999})
|
529
|
+
expect{subject.service_state(mock_service_name)}.to raise_error(Puppet::Error)
|
530
|
+
end
|
531
|
+
|
532
|
+
# We need to guard this section explicitly since rspec will always
|
533
|
+
# construct all examples, even if it isn't going to run them.
|
534
|
+
if Puppet.features.microsoft_windows?
|
535
|
+
{
|
536
|
+
:SERVICE_STOPPED => Puppet::Util::Windows::Service::SERVICE_STOPPED,
|
537
|
+
:SERVICE_PAUSED => Puppet::Util::Windows::Service::SERVICE_PAUSED,
|
538
|
+
:SERVICE_STOP_PENDING => Puppet::Util::Windows::Service::SERVICE_STOP_PENDING,
|
539
|
+
:SERVICE_PAUSE_PENDING => Puppet::Util::Windows::Service::SERVICE_PAUSE_PENDING,
|
540
|
+
:SERVICE_RUNNING => Puppet::Util::Windows::Service::SERVICE_RUNNING,
|
541
|
+
:SERVICE_CONTINUE_PENDING => Puppet::Util::Windows::Service::SERVICE_CONTINUE_PENDING,
|
542
|
+
:SERVICE_START_PENDING => Puppet::Util::Windows::Service::SERVICE_START_PENDING,
|
543
|
+
}.each do |state_name, state|
|
544
|
+
it "queries the service and returns #{state_name}" do
|
545
|
+
expect_successful_status_query_and_return({:dwCurrentState => state})
|
546
|
+
expect(subject.service_state(mock_service_name)).to eq(state_name)
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
describe "#service_start_type" do
|
554
|
+
context "when the service control manager cannot be opened" do
|
555
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
556
|
+
it "raises a puppet error" do
|
557
|
+
expect{ subject.service_start_type(mock_service_name) }.to raise_error(Puppet::Error)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
context "when the service cannot be opened" do
|
562
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
563
|
+
it "raises a puppet error" do
|
564
|
+
expect{ subject.service_start_type(mock_service_name) }.to raise_error(Puppet::Error)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
context "when the service can be opened" do
|
569
|
+
|
570
|
+
# We need to guard this section explicitly since rspec will always
|
571
|
+
# construct all examples, even if it isn't going to run them.
|
572
|
+
if Puppet.features.microsoft_windows?
|
573
|
+
{
|
574
|
+
:SERVICE_AUTO_START => Puppet::Util::Windows::Service::SERVICE_AUTO_START,
|
575
|
+
:SERVICE_BOOT_START => Puppet::Util::Windows::Service::SERVICE_BOOT_START,
|
576
|
+
:SERVICE_SYSTEM_START => Puppet::Util::Windows::Service::SERVICE_SYSTEM_START,
|
577
|
+
:SERVICE_DEMAND_START => Puppet::Util::Windows::Service::SERVICE_DEMAND_START,
|
578
|
+
:SERVICE_DISABLED => Puppet::Util::Windows::Service::SERVICE_DISABLED,
|
579
|
+
}.each do |start_type_name, start_type|
|
580
|
+
it "queries the service and returns the service start type #{start_type_name}" do
|
581
|
+
expect_successful_config_query_and_return({:dwStartType => start_type})
|
582
|
+
expect(subject.service_start_type(mock_service_name)).to eq(start_type_name)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
it "raises a puppet error if the service query fails" do
|
587
|
+
subject.expects(:QueryServiceConfigW).in_sequence(status_checks)
|
588
|
+
subject.expects(:QueryServiceConfigW).in_sequence(status_checks).returns(FFI::WIN32_FALSE)
|
589
|
+
expect{ subject.service_start_type(mock_service_name) }.to raise_error(Puppet::Error)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
describe "#set_startup_mode" do
|
595
|
+
let(:status_checks) { sequence('status_checks') }
|
596
|
+
|
597
|
+
context "when the service control manager cannot be opened" do
|
598
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
599
|
+
it "raises a puppet error" do
|
600
|
+
expect{ subject.set_startup_mode(mock_service_name, :SERVICE_DEMAND_START) }.to raise_error(Puppet::Error)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
context "when the service cannot be opened" do
|
605
|
+
let(:service) { FFI::Pointer::NULL_HANDLE }
|
606
|
+
it "raises a puppet error" do
|
607
|
+
expect{ subject.set_startup_mode(mock_service_name, :SERVICE_DEMAND_START) }.to raise_error(Puppet::Error)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
context "when the service can be opened" do
|
612
|
+
it "Raises an error on an unsuccessful change" do
|
613
|
+
subject.expects(:ChangeServiceConfigW).returns(FFI::WIN32_FALSE)
|
614
|
+
expect{ subject.set_startup_mode(mock_service_name, :SERVICE_DEMAND_START) }.to raise_error(Puppet::Error)
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
describe "#services" do
|
620
|
+
let(:pointer_sequence) { sequence('pointer_sequence') }
|
621
|
+
|
622
|
+
context "when the service control manager cannot be opened" do
|
623
|
+
let(:scm) { FFI::Pointer::NULL_HANDLE }
|
624
|
+
it "raises a puppet error" do
|
625
|
+
expect{ subject.services }.to raise_error(Puppet::Error)
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
context "when the service control manager is open" do
|
630
|
+
let(:cursor) { [ 'svc1', 'svc2', 'svc3' ] }
|
631
|
+
let(:svc1name_ptr) { mock() }
|
632
|
+
let(:svc2name_ptr) { mock() }
|
633
|
+
let(:svc3name_ptr) { mock() }
|
634
|
+
let(:svc1displayname_ptr) { mock() }
|
635
|
+
let(:svc2displayname_ptr) { mock() }
|
636
|
+
let(:svc3displayname_ptr) { mock() }
|
637
|
+
let(:svc1) { { :lpServiceName => svc1name_ptr, :lpDisplayName => svc1displayname_ptr, :ServiceStatusProcess => 'foo' } }
|
638
|
+
let(:svc2) { { :lpServiceName => svc2name_ptr, :lpDisplayName => svc2displayname_ptr, :ServiceStatusProcess => 'foo' } }
|
639
|
+
let(:svc3) { { :lpServiceName => svc3name_ptr, :lpDisplayName => svc3displayname_ptr, :ServiceStatusProcess => 'foo' } }
|
640
|
+
|
641
|
+
it "Raises an error if EnumServicesStatusExW fails" do
|
642
|
+
subject.expects(:EnumServicesStatusExW).in_sequence(pointer_sequence)
|
643
|
+
subject.expects(:EnumServicesStatusExW).in_sequence(pointer_sequence).returns(FFI::WIN32_FALSE)
|
644
|
+
expect{ subject.services }.to raise_error(Puppet::Error)
|
645
|
+
end
|
646
|
+
|
647
|
+
it "Reads the buffer using pointer arithmetic to create a hash of service entries" do
|
648
|
+
# the first read_dword is for reading the bytes required, let that return 3 too.
|
649
|
+
# the second read_dword will actually read the number of services returned
|
650
|
+
pointer.expects(:read_dword).twice.returns(3)
|
651
|
+
FFI::Pointer.expects(:new).with(subject::ENUM_SERVICE_STATUS_PROCESSW, pointer).returns(cursor)
|
652
|
+
subject::ENUM_SERVICE_STATUS_PROCESSW.expects(:new).in_sequence(pointer_sequence).with('svc1').returns(svc1)
|
653
|
+
subject::ENUM_SERVICE_STATUS_PROCESSW.expects(:new).in_sequence(pointer_sequence).with('svc2').returns(svc2)
|
654
|
+
subject::ENUM_SERVICE_STATUS_PROCESSW.expects(:new).in_sequence(pointer_sequence).with('svc3').returns(svc3)
|
655
|
+
svc1name_ptr.expects(:read_arbitrary_wide_string_up_to).returns('svc1')
|
656
|
+
svc2name_ptr.expects(:read_arbitrary_wide_string_up_to).returns('svc2')
|
657
|
+
svc3name_ptr.expects(:read_arbitrary_wide_string_up_to).returns('svc3')
|
658
|
+
svc1displayname_ptr.expects(:read_arbitrary_wide_string_up_to).returns('service 1')
|
659
|
+
svc2displayname_ptr.expects(:read_arbitrary_wide_string_up_to).returns('service 2')
|
660
|
+
svc3displayname_ptr.expects(:read_arbitrary_wide_string_up_to).returns('service 3')
|
661
|
+
expect(subject.services).to eq({
|
662
|
+
'svc1' => { :display_name => 'service 1', :service_status_process => 'foo' },
|
663
|
+
'svc2' => { :display_name => 'service 2', :service_status_process => 'foo' },
|
664
|
+
'svc3' => { :display_name => 'service 3', :service_status_process => 'foo' }
|
665
|
+
})
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|