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.

Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +12 -12
  4. data/Rakefile +9 -0
  5. data/lib/puppet/application.rb +5 -0
  6. data/lib/puppet/application/apply.rb +1 -0
  7. data/lib/puppet/application/master.rb +9 -7
  8. data/lib/puppet/application/script.rb +1 -1
  9. data/lib/puppet/defaults.rb +51 -31
  10. data/lib/puppet/etc.rb +20 -0
  11. data/lib/puppet/file_serving/fileset.rb +1 -1
  12. data/lib/puppet/functions.rb +123 -0
  13. data/lib/puppet/functions/new.rb +37 -53
  14. data/lib/puppet/functions/warning.rb +1 -1
  15. data/lib/puppet/loaders.rb +1 -0
  16. data/lib/puppet/parser/functions.rb +3 -1
  17. data/lib/puppet/parser/functions/sprintf.rb +12 -1
  18. data/lib/puppet/pops/evaluator/runtime3_converter.rb +16 -0
  19. data/lib/puppet/pops/evaluator/runtime3_support.rb +3 -4
  20. data/lib/puppet/pops/issues.rb +8 -0
  21. data/lib/puppet/pops/loader/loader.rb +2 -2
  22. data/lib/puppet/pops/loader/loader_paths.rb +3 -1
  23. data/lib/puppet/pops/loader/module_loaders.rb +1 -1
  24. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +62 -0
  25. data/lib/puppet/pops/loaders.rb +5 -21
  26. data/lib/puppet/pops/parser/heredoc_support.rb +1 -2
  27. data/lib/puppet/pops/parser/lexer2.rb +1 -1
  28. data/lib/puppet/pops/validation/checker4_0.rb +31 -6
  29. data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
  30. data/lib/puppet/property/keyvalue.rb +70 -8
  31. data/lib/puppet/provider/aix_object.rb +483 -0
  32. data/lib/puppet/provider/exec.rb +54 -57
  33. data/lib/puppet/provider/group/aix.rb +40 -115
  34. data/lib/puppet/provider/group/pw.rb +4 -8
  35. data/lib/puppet/provider/group/windows_adsi.rb +7 -4
  36. data/lib/puppet/provider/nameservice.rb +1 -25
  37. data/lib/puppet/provider/nameservice/directoryservice.rb +5 -3
  38. data/lib/puppet/provider/package/portage.rb +2 -2
  39. data/lib/puppet/provider/package/windows.rb +2 -2
  40. data/lib/puppet/provider/package/windows/exe_package.rb +3 -10
  41. data/lib/puppet/provider/package/zypper.rb +1 -1
  42. data/lib/puppet/provider/service/launchd.rb +19 -3
  43. data/lib/puppet/provider/service/windows.rb +49 -40
  44. data/lib/puppet/provider/user/aix.rb +180 -246
  45. data/lib/puppet/provider/user/windows_adsi.rb +9 -1
  46. data/lib/puppet/resource/catalog.rb +1 -5
  47. data/lib/puppet/type/augeas.rb +1 -1
  48. data/lib/puppet/type/exec.rb +16 -14
  49. data/lib/puppet/type/file.rb +2 -2
  50. data/lib/puppet/type/file/source.rb +9 -5
  51. data/lib/puppet/type/group.rb +65 -23
  52. data/lib/puppet/type/k5login.rb +2 -2
  53. data/lib/puppet/type/notify.rb +1 -1
  54. data/lib/puppet/type/package.rb +3 -6
  55. data/lib/puppet/type/resources.rb +12 -2
  56. data/lib/puppet/type/schedule.rb +8 -1
  57. data/lib/puppet/type/selboolean.rb +2 -2
  58. data/lib/puppet/type/selmodule.rb +3 -4
  59. data/lib/puppet/type/service.rb +2 -5
  60. data/lib/puppet/type/tidy.rb +1 -1
  61. data/lib/puppet/type/user.rb +15 -20
  62. data/lib/puppet/type/yumrepo.rb +2 -2
  63. data/lib/puppet/type/zone.rb +2 -2
  64. data/lib/puppet/util.rb +7 -3
  65. data/lib/puppet/util/execution.rb +15 -1
  66. data/lib/puppet/util/posix.rb +15 -0
  67. data/lib/puppet/util/storage.rb +12 -0
  68. data/lib/puppet/util/windows.rb +4 -2
  69. data/lib/puppet/util/windows/adsi.rb +235 -205
  70. data/lib/puppet/util/windows/process.rb +23 -3
  71. data/lib/puppet/util/windows/security.rb +14 -0
  72. data/lib/puppet/util/windows/service.rb +977 -0
  73. data/lib/puppet/util/windows/user.rb +3 -5
  74. data/lib/puppet/version.rb +1 -1
  75. data/locales/ja/puppet.po +705 -374
  76. data/locales/puppet.pot +485 -261
  77. data/man/man5/puppet.conf.5 +36 -15
  78. data/man/man8/puppet-agent.8 +1 -1
  79. data/man/man8/puppet-apply.8 +1 -1
  80. data/man/man8/puppet-ca.8 +1 -1
  81. data/man/man8/puppet-catalog.8 +1 -1
  82. data/man/man8/puppet-cert.8 +1 -1
  83. data/man/man8/puppet-certificate.8 +1 -1
  84. data/man/man8/puppet-certificate_request.8 +1 -1
  85. data/man/man8/puppet-certificate_revocation_list.8 +1 -1
  86. data/man/man8/puppet-config.8 +1 -1
  87. data/man/man8/puppet-describe.8 +1 -1
  88. data/man/man8/puppet-device.8 +1 -1
  89. data/man/man8/puppet-doc.8 +1 -1
  90. data/man/man8/puppet-epp.8 +1 -1
  91. data/man/man8/puppet-facts.8 +1 -1
  92. data/man/man8/puppet-filebucket.8 +1 -1
  93. data/man/man8/puppet-generate.8 +1 -1
  94. data/man/man8/puppet-help.8 +1 -1
  95. data/man/man8/puppet-key.8 +1 -1
  96. data/man/man8/puppet-lookup.8 +1 -1
  97. data/man/man8/puppet-man.8 +1 -1
  98. data/man/man8/puppet-master.8 +1 -1
  99. data/man/man8/puppet-module.8 +1 -1
  100. data/man/man8/puppet-node.8 +1 -1
  101. data/man/man8/puppet-parser.8 +1 -1
  102. data/man/man8/puppet-plugin.8 +1 -1
  103. data/man/man8/puppet-report.8 +1 -1
  104. data/man/man8/puppet-resource.8 +1 -1
  105. data/man/man8/puppet-script.8 +1 -1
  106. data/man/man8/puppet-status.8 +1 -1
  107. data/man/man8/puppet.8 +2 -2
  108. data/spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_input.out +1 -0
  109. data/spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_output.out +1 -0
  110. data/spec/fixtures/unit/provider/user/aix/aix_passwd_file.out +32 -0
  111. data/spec/integration/parser/collection_spec.rb +4 -8
  112. data/spec/integration/provider/service/windows_spec.rb +5 -5
  113. data/spec/integration/type/file_spec.rb +6 -6
  114. data/spec/integration/util/windows/adsi_spec.rb +6 -5
  115. data/spec/integration/util/windows/security_spec.rb +10 -7
  116. data/spec/integration/util/windows/user_spec.rb +37 -17
  117. data/spec/spec_helper.rb +0 -1
  118. data/spec/unit/application/apply_spec.rb +41 -2
  119. data/spec/unit/application/master_spec.rb +7 -0
  120. data/spec/unit/application_spec.rb +21 -3
  121. data/spec/unit/defaults_spec.rb +20 -0
  122. data/spec/unit/etc_spec.rb +25 -0
  123. data/spec/unit/file_serving/fileset_spec.rb +11 -11
  124. data/spec/unit/gettext/config_spec.rb +1 -1
  125. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +6 -6
  126. data/spec/unit/pops/loaders/loaders_spec.rb +40 -7
  127. data/spec/unit/pops/parser/parse_heredoc_spec.rb +16 -0
  128. data/spec/unit/pops/validator/validator_spec.rb +129 -10
  129. data/spec/unit/property/keyvalue_spec.rb +97 -6
  130. data/spec/unit/provider/aix_object_spec.rb +805 -0
  131. data/spec/unit/provider/group/aix_spec.rb +57 -0
  132. data/spec/unit/provider/group/pw_spec.rb +0 -6
  133. data/spec/unit/provider/group/windows_adsi_spec.rb +34 -35
  134. data/spec/unit/provider/nameservice/directoryservice_spec.rb +2 -2
  135. data/spec/unit/provider/package/windows/exe_package_spec.rb +3 -3
  136. data/spec/unit/provider/package/windows_spec.rb +4 -4
  137. data/spec/unit/provider/service/launchd_spec.rb +19 -0
  138. data/spec/unit/provider/service/windows_spec.rb +71 -78
  139. data/spec/unit/provider/user/aix_spec.rb +162 -116
  140. data/spec/unit/provider/user/windows_adsi_spec.rb +4 -4
  141. data/spec/unit/resource/catalog_spec.rb +2 -2
  142. data/spec/unit/ssl/certificate_authority_spec.rb +0 -1
  143. data/spec/unit/type/group_spec.rb +111 -13
  144. data/spec/unit/type/resources_spec.rb +18 -0
  145. data/spec/unit/util/execution_spec.rb +77 -0
  146. data/spec/unit/util/posix_spec.rb +28 -0
  147. data/spec/unit/util/storage_spec.rb +107 -0
  148. data/spec/unit/util/windows/adsi_spec.rb +108 -13
  149. data/spec/unit/util/windows/service_spec.rb +669 -0
  150. metadata +17 -5
  151. data/lib/puppet/provider/aixobject.rb +0 -392
  152. 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