puppet 6.0.2-x64-mingw32 → 6.0.3-x64-mingw32

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +11 -11
  4. data/lib/puppet/application.rb +5 -0
  5. data/lib/puppet/application/apply.rb +1 -0
  6. data/lib/puppet/application/script.rb +1 -1
  7. data/lib/puppet/application/ssl.rb +119 -49
  8. data/lib/puppet/defaults.rb +9 -27
  9. data/lib/puppet/face/node/clean.rb +0 -1
  10. data/lib/puppet/feature/base.rb +1 -1
  11. data/lib/puppet/file_serving/fileset.rb +1 -1
  12. data/lib/puppet/pops/validation/checker4_0.rb +4 -2
  13. data/lib/puppet/provider/package/windows.rb +2 -2
  14. data/lib/puppet/provider/package/windows/exe_package.rb +3 -10
  15. data/lib/puppet/provider/service/windows.rb +11 -3
  16. data/lib/puppet/provider/user/useradd.rb +2 -10
  17. data/lib/puppet/resource/catalog.rb +1 -5
  18. data/lib/puppet/ssl/host.rb +7 -9
  19. data/lib/puppet/transaction/persistence.rb +1 -1
  20. data/lib/puppet/type/package.rb +1 -1
  21. data/lib/puppet/type/user.rb +4 -1
  22. data/lib/puppet/util.rb +7 -3
  23. data/lib/puppet/util/execution.rb +1 -0
  24. data/lib/puppet/util/logging.rb +3 -2
  25. data/lib/puppet/util/windows/process.rb +6 -2
  26. data/lib/puppet/util/windows/security.rb +14 -0
  27. data/lib/puppet/util/windows/service.rb +217 -74
  28. data/lib/puppet/util/windows/user.rb +3 -5
  29. data/lib/puppet/version.rb +1 -1
  30. data/locales/ja/puppet.po +505 -276
  31. data/locales/puppet.pot +250 -111
  32. data/man/man5/puppet.conf.5 +8 -1
  33. data/man/man8/puppet-ssl.8 +22 -2
  34. data/man/man8/puppet.8 +1 -1
  35. data/spec/integration/parser/collection_spec.rb +4 -8
  36. data/spec/integration/type/file_spec.rb +6 -6
  37. data/spec/integration/util/windows/security_spec.rb +10 -7
  38. data/spec/integration/util/windows/user_spec.rb +37 -17
  39. data/spec/lib/puppet/test_ca.rb +1 -1
  40. data/spec/unit/agent_spec.rb +2 -2
  41. data/spec/unit/application/apply_spec.rb +41 -2
  42. data/spec/unit/application/face_base_spec.rb +1 -1
  43. data/spec/unit/application/ssl_spec.rb +160 -110
  44. data/spec/unit/application_spec.rb +29 -11
  45. data/spec/unit/configurer/downloader_spec.rb +1 -1
  46. data/spec/unit/configurer_spec.rb +5 -5
  47. data/spec/unit/face/node_spec.rb +1 -3
  48. data/spec/unit/file_serving/fileset_spec.rb +11 -11
  49. data/spec/unit/network/http/connection_spec.rb +2 -2
  50. data/spec/unit/pops/validator/validator_spec.rb +24 -10
  51. data/spec/unit/provider/package/windows/exe_package_spec.rb +3 -3
  52. data/spec/unit/provider/package/windows_spec.rb +4 -4
  53. data/spec/unit/provider/service/windows_spec.rb +21 -3
  54. data/spec/unit/provider/user/useradd_spec.rb +2 -2
  55. data/spec/unit/resource/catalog_spec.rb +2 -2
  56. data/spec/unit/ssl/host_spec.rb +1 -1
  57. data/spec/unit/transaction/persistence_spec.rb +4 -4
  58. data/spec/unit/util/execution_spec.rb +19 -1
  59. data/spec/unit/util/logging_spec.rb +58 -0
  60. data/spec/unit/util/windows/service_spec.rb +344 -191
  61. metadata +2 -2
@@ -168,11 +168,29 @@ describe Puppet::Util::Execution, if: !Puppet::Util::Platform.jruby? do
168
168
  },
169
169
  :close_handles => false
170
170
  )
171
-
171
+
172
172
  call_exec_windows('test command', { :cwd => cwd }, @stdin, @stdout, @stderr)
173
173
  end
174
174
  end
175
175
 
176
+ context 'suppress_window option' do
177
+ let(:cwd) { 'cwd' }
178
+ it "should execute the command in the specified working directory" do
179
+ Process.expects(:create).with(
180
+ :command_line => "test command",
181
+ :startup_info => {
182
+ :stdin => @stdin,
183
+ :stdout => @stdout,
184
+ :stderr => @stderr
185
+ },
186
+ :close_handles => false,
187
+ :creation_flags => Puppet::Util::Windows::Process::CREATE_NO_WINDOW
188
+ )
189
+
190
+ call_exec_windows('test command', { :suppress_window => true }, @stdin, @stdout, @stderr)
191
+ end
192
+ end
193
+
176
194
  it "should return the process info of the child process" do
177
195
  expect(call_exec_windows('test command', {}, @stdin, @stdout, @stderr)).to eq(proc_info_stub)
178
196
  end
@@ -99,6 +99,64 @@ describe Puppet::Util::Logging do
99
99
  end
100
100
  end
101
101
 
102
+ describe "log_exception" do
103
+ context "when requesting a debug level it is logged at debug" do
104
+ it "the exception is a ParseErrorWithIssue and message is :default" do
105
+ Puppet::Util::Log.expects(:create).with do |args|
106
+ expect(args[:message]).to eq("Test")
107
+ expect(args[:level]).to eq(:debug)
108
+ end
109
+
110
+ begin
111
+ raise Puppet::ParseErrorWithIssue, "Test"
112
+ rescue Puppet::ParseErrorWithIssue => err
113
+ Puppet.log_exception(err, :default, level: :debug)
114
+ end
115
+ end
116
+
117
+ it "the exception is something else" do
118
+ Puppet::Util::Log.expects(:create).with do |args|
119
+ expect(args[:message]).to eq("Test")
120
+ expect(args[:level]).to eq(:debug)
121
+ end
122
+
123
+ begin
124
+ raise Puppet::Error, "Test"
125
+ rescue Puppet::Error => err
126
+ Puppet.log_exception(err, :default, level: :debug)
127
+ end
128
+ end
129
+ end
130
+
131
+ context "no log level is requested it defaults to err" do
132
+ it "the exception is a ParseErrorWithIssue and message is :default" do
133
+ Puppet::Util::Log.expects(:create).with do |args|
134
+ expect(args[:message]).to eq("Test")
135
+ expect(args[:level]).to eq(:err)
136
+ end
137
+
138
+ begin
139
+ raise Puppet::ParseErrorWithIssue, "Test"
140
+ rescue Puppet::ParseErrorWithIssue => err
141
+ Puppet.log_exception(err)
142
+ end
143
+ end
144
+
145
+ it "the exception is something else" do
146
+ Puppet::Util::Log.expects(:create).with do |args|
147
+ expect(args[:message]).to eq("Test")
148
+ expect(args[:level]).to eq(:err)
149
+ end
150
+
151
+ begin
152
+ raise Puppet::Error, "Test"
153
+ rescue Puppet::Error => err
154
+ Puppet.log_exception(err)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
102
160
  describe "when sending a deprecation warning" do
103
161
  it "does not log a message when deprecation warnings are disabled" do
104
162
  Puppet.expects(:[]).with(:disable_warnings).returns %w[deprecations]
@@ -10,6 +10,10 @@ describe "Puppet::Util::Windows::Service", :if => Puppet.features.microsoft_wind
10
10
  .returns("fake error!")
11
11
  end
12
12
 
13
+ def service_state_str(state)
14
+ Puppet::Util::Windows::Service::SERVICE_STATES[state].to_s
15
+ end
16
+
13
17
  # The following should emulate a successful call to the private function
14
18
  # query_status that returns the value of query_return. This should give
15
19
  # us a way to mock changes in service status.
@@ -21,6 +25,12 @@ describe "Puppet::Util::Windows::Service", :if => Puppet.features.microsoft_wind
21
25
  subject::SERVICE_STATUS_PROCESS.expects(:new).in_sequence(status_checks).returns(query_return)
22
26
  end
23
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
+
24
34
  # The following should emulate a successful call to the private function
25
35
  # query_config that returns the value of query_return. This should give
26
36
  # us a way to mock changes in service configuration.
@@ -42,8 +52,6 @@ describe "Puppet::Util::Windows::Service", :if => Puppet.features.microsoft_wind
42
52
  before do
43
53
  subject.stubs(:QueryServiceStatusEx).returns(1)
44
54
  subject.stubs(:QueryServiceConfigW).returns(1)
45
- subject.stubs(:StartServiceW).returns(1)
46
- subject.stubs(:ControlService).returns(1)
47
55
  subject.stubs(:ChangeServiceConfigW).returns(1)
48
56
  subject.stubs(:OpenSCManagerW).returns(scm)
49
57
  subject.stubs(:OpenServiceW).returns(service)
@@ -90,262 +98,407 @@ describe "Puppet::Util::Windows::Service", :if => Puppet.features.microsoft_wind
90
98
  end
91
99
  end
92
100
 
93
- describe "#start" do
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
94
111
 
95
- context "when the service control manager cannot be opened" do
96
- let(:scm) { FFI::Pointer::NULL_HANDLE }
97
- it "raises a puppet error" do
98
- expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
99
- end
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)
100
119
  end
101
120
 
102
- context "when the service cannot be opened" do
103
- let(:service) { FFI::Pointer::NULL_HANDLE }
104
- it "raises a puppet error" do
105
- expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
106
- end
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
107
164
  end
108
165
 
109
- context "when the service can be opened and is in the stopped state" do
110
- before do
111
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
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
+ )
112
173
  end
113
174
 
114
- it "Starts the service once the service reports SERVICE_RUNNING" do
115
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
116
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
117
- subject.start(mock_service_name)
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)
118
215
  end
216
+ end
119
217
 
120
- it "Raises an error if after calling StartServiceW the service never transitions to RUNNING or START_PENDING" do
121
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_PAUSED}).times(31)
122
- expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
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)
123
221
  end
124
222
 
125
- it "raises a puppet error if StartServiceW returns false" do
126
- subject.expects(:StartServiceW).returns(FFI::WIN32_FALSE)
127
- expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
223
+ include_examples "a service action waiting on a pending transition", pending_state do
224
+ let(:action) { action }
128
225
  end
129
226
  end
130
227
 
131
- context "when the service hasn't stopped yet:" do
132
- it "waits, then queries again until SERVICE_STOPPED" do
133
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
134
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
135
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 50})
136
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
137
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
138
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
139
- subject.expects(:sleep).with(3).twice
140
- subject.start(mock_service_name)
141
- end
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
142
248
 
143
- it "waits for at least 1 second if wait hint/10 is < 1 second" do
144
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
145
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 1})
146
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
147
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
148
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
149
- subject.expects(:sleep).with(1)
150
- subject.start(mock_service_name)
249
+ include_examples "a service action waiting on a pending transition", unsafe_pending_state do
250
+ let(:action) { action }
251
+ end
151
252
  end
253
+ end
152
254
 
153
- it "waits for at most 10 seconds if wait hint/10 is > 10 seconds" do
154
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
155
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 1000000, :dwCheckPoint => 1})
156
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
157
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
158
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
159
- subject.expects(:sleep).with(10)
160
- subject.start(mock_service_name)
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
161
264
  end
162
265
 
163
- it "raises a puppet error if the service query fails" do
164
- subject.expects(:QueryServiceStatusEx).in_sequence(status_checks).returns(1)
266
+ it "raises a Puppet::Error if the service query fails" do
165
267
  subject.expects(:QueryServiceStatusEx).in_sequence(status_checks).returns(FFI::WIN32_FALSE)
166
- expect{subject.start(mock_service_name)}.to raise_error(Puppet::Error)
167
- end
168
-
169
- it "Does not raise an error if the service makes progress" do
170
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 0})
171
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 0})
172
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 2})
173
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 30})
174
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 50})
175
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 98})
176
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
177
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
178
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
179
- expect{subject.start(mock_service_name)}.to_not raise_error
180
- end
181
- end
182
-
183
- context "when the service ends up still in STOPPED:" do
184
- it "waits, then queries again until RUNNING" do
185
- # these will be before the call to controlService
186
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
187
- # everything from here on will be _after_ the call to ControlService
188
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
189
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
190
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 50})
191
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
192
- subject.start(mock_service_name)
193
- end
194
268
 
195
- it "raises a puppet error if the services never exits the RUNNING state" do
196
- # these will be before the call to controlService
197
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
198
- # the number of times here is a little strange: there are 31 status queries sleeps because there will be a 31st query
199
- # that is the final query where the command has reached the wait hint and it's time to error
200
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED}).times(31)
201
- subject.expects(:sleep).times(30).with(1)
202
- expect{subject.start(mock_service_name)}.to raise_error(Puppet::Error)
269
+ expect { subject.send(action, mock_service_name) }.to raise_error(Puppet::Error)
203
270
  end
204
- end
205
271
 
206
- context "when the service ends up in START_PENDING:" do
207
- before(:each) do
208
- # these will be before the call to StartService
209
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
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)
210
282
  end
211
283
 
212
- it "waits, then queries again until SERVICE_RUNNING" do
213
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
214
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 50})
215
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
216
- subject.start(mock_service_name)
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
217
292
  end
218
293
 
219
- it "waits for at least 1 second if wait hint/10 is < 1 second" do
220
- # the first call is executed in wait_for_state, which we aren't testing here
221
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING})
222
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 2})
223
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
224
- subject.expects(:sleep).with(1)
225
- subject.start(mock_service_name)
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)
226
300
  end
301
+ end
302
+ end
227
303
 
228
- it "waits for at most 10 seconds if wait hint/10 is > 10 seconds" do
229
- # the first call is executed in wait_for_state, which we aren't testing here
230
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING})
231
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 1000000, :dwCheckPoint => 1})
232
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
233
- subject.expects(:sleep).with(10)
234
- subject.start(mock_service_name)
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)
235
323
  end
324
+ end
236
325
 
237
- it "raises a puppet error if the services configured dwWaitHint is 0, 30 seconds have passed and dwCheckPoint hasn't increased" do
238
- # the first call is executed in wait_for_state, which we aren't testing here
239
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING})
240
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 0}).times(31)
241
- subject.expects(:sleep).times(30).with(1)
242
- expect{subject.start(mock_service_name)}.to raise_error(Puppet::Error)
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)
243
330
  end
331
+ end
244
332
 
245
- it "raises a puppet error if the service's configured dwWaitHint has passed and dwCheckPoint hasn't increased" do
246
- # the first call is executed in wait_for_state, which we aren't testing here
247
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING})
248
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 40000, :dwCheckPoint => 0}).times(11)
249
- subject.expects(:sleep).times(10).with(4)
250
- expect{subject.start(mock_service_name)}.to raise_error(Puppet::Error)
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
251
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)
252
357
 
253
- it "Does not raise an error if the service makes progress" do
254
- # the first call is executed in wait_for_state, which we aren't testing here
255
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING})
256
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 0})
257
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 2})
258
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 30})
259
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 50})
260
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_START_PENDING, :dwWaitHint => 0, :dwCheckPoint => 98})
261
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
262
- subject.expects(:sleep).times(5).with(1)
263
- expect{subject.start(mock_service_name)}.to_not raise_error
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)
264
370
  end
265
371
  end
266
372
  end
267
373
 
268
374
  describe "#stop" do
375
+ next unless Puppet.features.microsoft_windows?
376
+
269
377
  context "when the service control manager cannot be opened" do
270
378
  let(:scm) { FFI::Pointer::NULL_HANDLE }
271
379
  it "raises a puppet error" do
272
- expect{ subject.stop(mock_service_name) }.to raise_error(Puppet::Error)
380
+ expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
273
381
  end
274
382
  end
275
383
 
276
384
  context "when the service cannot be opened" do
277
385
  let(:service) { FFI::Pointer::NULL_HANDLE }
278
386
  it "raises a puppet error" do
279
- expect{ subject.stop(mock_service_name) }.to raise_error(Puppet::Error)
387
+ expect{ subject.start(mock_service_name) }.to raise_error(Puppet::Error)
280
388
  end
281
389
  end
282
390
 
283
- context "when the service can be opened and is in the running state:" do
284
- before do
285
- # this will be before the call to controlService
286
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
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)
287
411
  end
288
- it "raises a puppet error if ControlService returns false" do
289
- subject.expects(:ControlService).returns(FFI::WIN32_FALSE)
290
- expect{ subject.stop(mock_service_name) }.to raise_error(Puppet::Error)
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)
291
422
  end
292
423
  end
424
+ end
293
425
 
294
- # No need to retest the wait hint functionality itself here, since
295
- # both stop and start use the wait_for_pending_transition helper
296
- # which is tested in the start unit tests.
297
- context "when the service is already in stop pending or stopped" do
298
- it "waits for the service to stop and then exits immediately" do
299
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
300
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 5})
301
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
302
- subject.stop(mock_service_name)
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)
303
433
  end
304
434
  end
305
435
 
306
- context "when the service ends up in STOP_PENDING:" do
307
- before(:each) do
308
- # this will be before the call to controlService
309
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
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)
310
440
  end
441
+ end
311
442
 
312
- it "waits, then queries again until SERVICE_STOPPED" do
313
- # the first call is to wait_for_state, which we don't test here
314
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING})
315
- # everything from here on will be _after_ the call to ControlService
316
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
317
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 50})
318
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
319
- subject.stop(mock_service_name)
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
320
464
  end
321
465
 
322
- it "raises a puppet error if the services never exits the STOP_PENDING state" do
323
- # the first call is to wait_for_state, which we don't test here
324
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING})
325
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 0, :dwCheckPoint => 0}).times(31)
326
- expect{subject.stop(mock_service_name)}.to raise_error(Puppet::Error)
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
327
481
  end
328
- end
329
482
 
330
- context "when the service ends up still in RUNNING:" do
331
- before(:each) do
332
- # this will be before the call to controlService
333
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING})
334
- end
335
- it "waits, then queries again until SERVICE_STOPPED" do
336
- # the first call is to wait_for_state, which we don't test here
337
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING, :dwWaitHint => 0, :dwCheckPoint => 0}).times(10)
338
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 1})
339
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOP_PENDING, :dwWaitHint => 30000, :dwCheckPoint => 50})
340
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_STOPPED})
341
- subject.stop(mock_service_name)
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)
342
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)
343
500
 
344
- it "raises a puppet error if the services never exits the RUNNING state" do
345
- # the first call is to wait_for_state, which we don't test here
346
- expect_successful_status_query_and_return({:dwCurrentState => subject::SERVICE_RUNNING, :dwWaitHint => 0, :dwCheckPoint => 0}).times(31)
347
- subject.expects(:sleep).times(30).with(1)
348
- expect{subject.stop(mock_service_name)}.to raise_error(Puppet::Error)
501
+ subject.resume(mock_service_name)
349
502
  end
350
503
  end
351
504
  end