resque-scheduler 4.2.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of resque-scheduler might be problematic. Click here for more details.

Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/HISTORY.md +7 -0
  4. data/README.md +7 -6
  5. data/{bin → exe}/resque-scheduler +0 -0
  6. data/lib/resque/scheduler/env.rb +5 -1
  7. data/lib/resque/scheduler/version.rb +1 -1
  8. data/resque-scheduler.gemspec +21 -7
  9. metadata +12 -53
  10. data/.gitignore +0 -17
  11. data/.rubocop.yml +0 -18
  12. data/.rubocop_todo.yml +0 -71
  13. data/.simplecov +0 -3
  14. data/.travis.yml +0 -26
  15. data/.vagrant-provision-as-vagrant.sh +0 -15
  16. data/.vagrant-provision.sh +0 -23
  17. data/.vagrant-skel/bash_profile +0 -7
  18. data/.vagrant-skel/bashrc +0 -7
  19. data/Vagrantfile +0 -14
  20. data/examples/Rakefile +0 -2
  21. data/examples/config/initializers/resque-web.rb +0 -37
  22. data/examples/dynamic-scheduling/README.md +0 -28
  23. data/examples/dynamic-scheduling/app/jobs/fix_schedules_job.rb +0 -52
  24. data/examples/dynamic-scheduling/app/jobs/send_email_job.rb +0 -9
  25. data/examples/dynamic-scheduling/app/models/user.rb +0 -16
  26. data/examples/dynamic-scheduling/config/resque.yml +0 -4
  27. data/examples/dynamic-scheduling/config/static_schedule.yml +0 -7
  28. data/examples/dynamic-scheduling/lib/tasks/resque.rake +0 -48
  29. data/examples/run-resque-web +0 -3
  30. data/script/migrate_to_timestamps_set.rb +0 -16
  31. data/tasks/resque_scheduler.rake +0 -2
  32. data/test/cli_test.rb +0 -231
  33. data/test/delayed_queue_test.rb +0 -925
  34. data/test/env_test.rb +0 -47
  35. data/test/multi_process_test.rb +0 -125
  36. data/test/resque-web_test.rb +0 -364
  37. data/test/scheduler_args_test.rb +0 -222
  38. data/test/scheduler_hooks_test.rb +0 -55
  39. data/test/scheduler_locking_test.rb +0 -316
  40. data/test/scheduler_setup_test.rb +0 -141
  41. data/test/scheduler_task_test.rb +0 -72
  42. data/test/scheduler_test.rb +0 -473
  43. data/test/test_helper.rb +0 -147
  44. data/test/util_test.rb +0 -17
@@ -1,47 +0,0 @@
1
- # vim:fileencoding=utf-8
2
- require_relative 'test_helper'
3
-
4
- context 'Env' do
5
- def new_env(options = {})
6
- Resque::Scheduler::Env.new(options)
7
- end
8
-
9
- test 'daemonizes when background is true' do
10
- Process.expects(:daemon)
11
- env = new_env(background: true)
12
- env.setup
13
- end
14
-
15
- test 'reconnects redis when background is true' do
16
- Process.stubs(:daemon)
17
- mock_redis_client = mock('redis_client')
18
- mock_redis = mock('redis')
19
- mock_redis.expects(:client).returns(mock_redis_client)
20
- mock_redis_client.expects(:reconnect)
21
- Resque.expects(:redis).returns(mock_redis)
22
- env = new_env(background: true)
23
- env.setup
24
- end
25
-
26
- test 'aborts when background is given and Process does not support daemon' do
27
- Process.stubs(:daemon)
28
- Process.expects(:respond_to?).with('daemon').returns(false)
29
- env = new_env(background: true)
30
- env.expects(:abort)
31
- env.setup
32
- end
33
-
34
- test 'keep set config if no option given' do
35
- Resque::Scheduler.configure { |c| c.dynamic = true }
36
- env = new_env
37
- env.setup
38
- assert_equal(true, Resque::Scheduler.dynamic)
39
- end
40
-
41
- test 'override config if option given' do
42
- Resque::Scheduler.configure { |c| c.dynamic = true }
43
- env = new_env(dynamic: false)
44
- env.setup
45
- assert_equal(false, Resque::Scheduler.dynamic)
46
- end
47
- end
@@ -1,125 +0,0 @@
1
- # vim:fileencoding=utf-8
2
- require_relative 'test_helper'
3
-
4
- context 'Multi Process' do
5
- test 'setting schedule= from many process does not corrupt the schedules' do
6
- # more info on why we're not using threads:
7
- # https://github.com/resque/resque-scheduler/pull/439#discussion_r16788812
8
- omit('forking is not supported by jruby but this behaviour' \
9
- ' is best tested using forks') if RUBY_ENGINE == 'jruby'
10
- schedules_1 = {}
11
- schedules_2 = {}
12
- schedules = []
13
- pids = []
14
-
15
- # This number may need to be increased if this test is not failing
16
- processes = 100
17
-
18
- schedule_count = 300
19
-
20
- schedule_count.times do |n|
21
- schedules_1["1_job_#{n}"] = { cron: '0 1 0 0 0' }
22
- schedules_2["2_job_#{n}"] = { cron: '0 1 0 0 0' }
23
- end
24
-
25
- processes.times do |n|
26
- pids << fork_with_marshalled_pipe_and_result do
27
- sleep n * 0.1
28
- Resque.schedule = n.even? ? schedules_2 : schedules_1
29
- Resque.schedule
30
- end
31
- end
32
-
33
- schedules += get_results_from_children(pids)
34
-
35
- assert_equal processes, schedules.size,
36
- 'missing some schedules, did a process die?'
37
- schedules.each_with_index do |schedule, i|
38
- assert_equal schedule_count, schedule.size,
39
- "schedule count is incorrect (schedule[#{i}]: #{schedule})"
40
- end
41
- end
42
-
43
- test 'concurrent shutdowns and startups do not corrupt the schedule' do
44
- omit('forking is not supported by jruby but this behaviour' \
45
- ' is best tested using forks') if RUBY_ENGINE == 'jruby'
46
- counts = []
47
- children = []
48
-
49
- processes = 40
50
-
51
- schedules = {}
52
- schedule_count = 300
53
- schedule_count.times do |n|
54
- schedules["job_#{n}"] = { 'cron' => '0 1 0 0 0' }
55
- end
56
-
57
- Resque.schedule = schedules
58
-
59
- processes.times do |n|
60
- children << fork_with_marshalled_pipe_and_result do
61
- sleep Random.rand(3) * 0.1
62
- if n.even?
63
- Resque.schedule = schedules
64
- Resque.schedule.size
65
- else
66
- Resque::Scheduler.before_shutdown
67
- nil
68
- end
69
- end
70
- end
71
-
72
- counts += get_results_from_children(children).compact
73
-
74
- assert_equal processes / 2, counts.size,
75
- 'missing some counts, did a process die?'
76
- counts.each_with_index do |c, i|
77
- assert_equal schedule_count, c, "schedule count is incorrect (c: #{i})"
78
- end
79
- end
80
-
81
- private
82
-
83
- def fork_with_marshalled_pipe_and_result
84
- pipe_read, pipe_write = IO.pipe
85
- pid = fork do
86
- pipe_read.close
87
- result = begin
88
- [yield, nil]
89
- rescue StandardError => exc
90
- [nil, exc]
91
- end
92
- pipe_write.syswrite(Marshal.dump(result))
93
- # exit true the process to get around fork issues on minitest 5
94
- # see https://github.com/seattlerb/minitest/issues/467
95
- Process.exit!(true)
96
- end
97
- pipe_write.close
98
-
99
- [pid, pipe_read]
100
- end
101
-
102
- def get_results_from_children(children)
103
- results = []
104
- children.each do |pid, pipe|
105
- wait_for_child_process_to_terminate(pid)
106
-
107
- raise "forked process failed with #{$CHILD_STATUS}" unless $CHILD_STATUS.success?
108
- result, exc = Marshal.load(pipe.read)
109
- raise exc if exc
110
- results << result
111
- end
112
- results
113
- end
114
-
115
- def wait_for_child_process_to_terminate(pid = -1, timeout = 30)
116
- Timeout.timeout(timeout) do
117
- Process.wait(pid)
118
- end
119
- rescue Timeout::Error
120
- Process.kill('KILL', pid)
121
- # collect status so it doesn't stick around as zombie process
122
- Process.wait(pid)
123
- flunk 'Child process did not terminate in time.'
124
- end
125
- end
@@ -1,364 +0,0 @@
1
- # vim:fileencoding=utf-8
2
- require_relative 'test_helper'
3
-
4
- require 'resque/server/test_helper'
5
-
6
- context 'on GET to /schedule' do
7
- setup { get '/schedule' }
8
-
9
- test('is 200') { assert last_response.ok? }
10
- end
11
-
12
- context 'on GET to /schedule with scheduled jobs' do
13
- setup do
14
- Resque::Scheduler.env = 'production'
15
- Resque.schedule = {
16
- 'some_ivar_job' => {
17
- 'cron' => '* * * * *',
18
- 'class' => 'SomeIvarJob',
19
- 'args' => '/tmp',
20
- 'rails_env' => 'production'
21
- },
22
- 'some_other_job' => {
23
- 'every' => ['1m', ['1h']],
24
- 'queue' => 'high',
25
- 'custom_job_class' => 'SomeOtherJob',
26
- 'args' => {
27
- 'b' => 'blah'
28
- }
29
- },
30
- 'some_fancy_job' => {
31
- 'every' => ['1m'],
32
- 'queue' => 'fancy',
33
- 'class' => 'SomeFancyJob',
34
- 'args' => 'sparkles',
35
- 'rails_env' => 'fancy'
36
- },
37
- 'shared_env_job' => {
38
- 'cron' => '* * * * *',
39
- 'class' => 'SomeSharedEnvJob',
40
- 'args' => '/tmp',
41
- 'rails_env' => 'fancy, production'
42
- }
43
- }
44
- Resque::Scheduler.load_schedule!
45
- get '/schedule'
46
- end
47
-
48
- test('is 200') { assert last_response.ok? }
49
-
50
- test 'see the scheduled job' do
51
- assert last_response.body.include?('SomeIvarJob')
52
- end
53
-
54
- test 'include(highlight) jobs for other envs' do
55
- assert last_response.body.include?('SomeFancyJob')
56
- end
57
-
58
- test 'includes job used in multiple environments' do
59
- assert last_response.body.include?('SomeSharedEnvJob')
60
- end
61
-
62
- test 'allows delete when dynamic' do
63
- Resque::Scheduler.stubs(:dynamic).returns(true)
64
- get '/schedule'
65
-
66
- assert last_response.body.include?('Delete')
67
- end
68
-
69
- test "doesn't allow delete when static" do
70
- Resque::Scheduler.stubs(:dynamic).returns(false)
71
- get '/schedule'
72
-
73
- assert !last_response.body.include?('Delete')
74
- end
75
- end
76
-
77
- context 'on GET to /delayed' do
78
- setup { get '/delayed' }
79
-
80
- test('is 200') { assert last_response.ok? }
81
- end
82
-
83
- context 'on GET to /delayed/jobs/:klass' do
84
- setup do
85
- @t = Time.now + 3600
86
- Resque.enqueue_at(@t, SomeIvarJob, 'foo', 'bar')
87
- get(
88
- URI('/delayed/jobs/SomeIvarJob?args=' <<
89
- URI.encode(%w(foo bar).to_json)).to_s
90
- )
91
- end
92
-
93
- test('is 200') { assert last_response.ok? }
94
-
95
- test 'see the scheduled job' do
96
- assert last_response.body.include?(@t.to_s)
97
- end
98
-
99
- context 'with a namespaced class' do
100
- setup do
101
- @t = Time.now + 3600
102
- module Foo
103
- class Bar
104
- def self.queue
105
- 'bar'
106
- end
107
- end
108
- end
109
- Resque.enqueue_at(@t, Foo::Bar, 'foo', 'bar')
110
- get(
111
- URI('/delayed/jobs/Foo::Bar?args=' <<
112
- URI.encode(%w(foo bar).to_json)).to_s
113
- )
114
- end
115
-
116
- test('is 200') { assert last_response.ok? }
117
-
118
- test 'see the scheduled job' do
119
- assert last_response.body.include?(@t.to_s)
120
- end
121
- end
122
- end
123
-
124
- module Test
125
- RESQUE_SCHEDULE = {
126
- 'job_without_params' => {
127
- 'cron' => '* * * * *',
128
- 'class' => 'JobWithoutParams',
129
- 'args' => {
130
- 'host' => 'localhost'
131
- },
132
- 'rails_env' => 'production'
133
- },
134
- 'job_with_params' => {
135
- 'every' => '1m',
136
- 'class' => 'JobWithParams',
137
- 'args' => {
138
- 'host' => 'localhost'
139
- },
140
- 'parameters' => {
141
- 'log_level' => {
142
- 'description' => 'The level of logging',
143
- 'default' => 'warn'
144
- }
145
- }
146
- }
147
- }.freeze
148
- end
149
-
150
- context 'POST /schedule/requeue' do
151
- setup do
152
- Resque.schedule = Test::RESQUE_SCHEDULE
153
- Resque::Scheduler.load_schedule!
154
- end
155
-
156
- test 'job without params' do
157
- # Regular jobs without params should redirect to /overview
158
- job_name = 'job_without_params'
159
- Resque::Scheduler.stubs(:enqueue_from_config)
160
- .once.with(Resque.schedule[job_name])
161
-
162
- post '/schedule/requeue', 'job_name' => job_name
163
- follow_redirect!
164
- assert_equal 'http://example.org/overview', last_request.url
165
- assert last_response.ok?
166
- end
167
-
168
- test 'job with params' do
169
- # If a job has params defined,
170
- # it should render the template with a form for the job params
171
- job_name = 'job_with_params'
172
- post '/schedule/requeue', 'job_name' => job_name
173
-
174
- assert last_response.ok?, last_response.errors
175
- assert last_response.body.include?('This job requires parameters')
176
- assert last_response.body.include?(
177
- %(<input type="hidden" name="job_name" value="#{job_name}">)
178
- )
179
-
180
- Resque.schedule[job_name]['parameters'].each do |_param_name, param_config|
181
- assert last_response.body.include?(
182
- '<span style="border-bottom:1px dotted;" ' <<
183
- %[title="#{param_config['description']}">(?)</span>]
184
- )
185
- assert last_response.body.include?(
186
- '<input type="text" name="log_level" ' <<
187
- %(value="#{param_config['default']}">)
188
- )
189
- end
190
- end
191
- end
192
-
193
- context 'POST /schedule/requeue_with_params' do
194
- setup do
195
- Resque.schedule = Test::RESQUE_SCHEDULE
196
- Resque::Scheduler.load_schedule!
197
- end
198
-
199
- test 'job with params' do
200
- job_name = 'job_with_params'
201
- log_level = 'error'
202
-
203
- job_config = Resque.schedule[job_name]
204
- args = job_config['args'].merge('log_level' => log_level)
205
- job_config = job_config.merge('args' => args)
206
-
207
- Resque::Scheduler.stubs(:enqueue_from_config).once.with(job_config)
208
-
209
- post '/schedule/requeue_with_params',
210
- 'job_name' => job_name,
211
- 'log_level' => log_level
212
-
213
- follow_redirect!
214
- assert_equal 'http://example.org/overview', last_request.url
215
-
216
- assert last_response.ok?, last_response.errors
217
- end
218
- end
219
-
220
- context 'on POST to /delayed/search' do
221
- setup do
222
- t = Time.now + 60
223
- Resque.enqueue_at(t, SomeIvarJob, 'string arg')
224
- Resque.enqueue(SomeQuickJob)
225
- end
226
-
227
- test 'should find matching scheduled job' do
228
- post '/delayed/search', 'search' => 'ivar'
229
- assert last_response.status == 200
230
- assert last_response.body.include?('SomeIvarJob')
231
- end
232
-
233
- test 'the form should encode string params' do
234
- post '/delayed/search', 'search' => 'ivar'
235
- assert_match('value="[&quot;string arg&quot;]', last_response.body)
236
- end
237
-
238
- test 'should find matching queued job' do
239
- post '/delayed/search', 'search' => 'quick'
240
- assert last_response.status == 200
241
- assert last_response.body.include?('SomeQuickJob')
242
- end
243
- end
244
-
245
- context 'on POST to /delayed/cancel_now' do
246
- setup do
247
- Resque.reset_delayed_queue
248
- Resque.enqueue_at(Time.now + 10, SomeIvarJob, 'arg')
249
- Resque.enqueue_at(Time.now + 100, SomeQuickJob)
250
- end
251
-
252
- test 'removes the specified job' do
253
- job_timestamp, *remaining = Resque.delayed_queue_peek(0, 10)
254
- assert_equal 1, remaining.size
255
-
256
- post '/delayed/cancel_now',
257
- 'timestamp' => job_timestamp,
258
- 'klass' => SomeIvarJob.name,
259
- 'args' => Resque.encode(['arg'])
260
-
261
- assert_equal 302, last_response.status
262
- assert_equal remaining, Resque.delayed_queue_peek(0, 10)
263
- end
264
-
265
- test 'does not remove the job if the params do not match' do
266
- timestamps = Resque.delayed_queue_peek(0, 10)
267
-
268
- post '/delayed/cancel_now',
269
- 'timestamp' => timestamps.first,
270
- 'klass' => SomeIvarJob.name
271
-
272
- assert_equal 302, last_response.status
273
- assert_equal timestamps, Resque.delayed_queue_peek(0, 10)
274
- end
275
-
276
- test 'redirects to overview' do
277
- post '/delayed/cancel_now'
278
- assert last_response.status == 302
279
- assert last_response.header['Location'].include? '/delayed'
280
- end
281
- end
282
-
283
- context 'on POST to /delayed/clear' do
284
- setup { post '/delayed/clear' }
285
-
286
- test 'redirects to delayed' do
287
- assert last_response.status == 302
288
- assert last_response.header['Location'].include? '/delayed'
289
- end
290
- end
291
-
292
- context 'on POST to /delayed/queue_now' do
293
- setup { post '/delayed/queue_now', timestamp: 0 }
294
-
295
- test 'returns ok status' do
296
- assert last_response.status == 200
297
- end
298
- end
299
-
300
- context 'on GET to /delayed/:timestamp' do
301
- setup { get '/delayed/1234567890' }
302
-
303
- test 'shows delayed_timestamp view' do
304
- assert last_response.status == 200
305
- end
306
- end
307
-
308
- context 'DELETE /schedule when dynamic' do
309
- setup do
310
- Resque.schedule = Test::RESQUE_SCHEDULE
311
- Resque::Scheduler.load_schedule!
312
- Resque::Scheduler.stubs(:dynamic).returns(true)
313
- end
314
-
315
- test 'redirects to schedule page' do
316
- delete '/schedule'
317
-
318
- status = last_response.status
319
- redirect_location = last_response.original_headers['Location']
320
- response_status_msg = "Expected response to be a 302, but was a #{status}."
321
- redirect_msg = "Redirect to #{redirect_location} instead of /schedule."
322
-
323
- assert status == 302, response_status_msg
324
- assert_match %r{/schedule/?$}, redirect_location, redirect_msg
325
- end
326
-
327
- test 'does not show the deleted job' do
328
- delete '/schedule', job_name: 'job_with_params'
329
- follow_redirect!
330
-
331
- msg = 'The job should not have been shown on the /schedule page.'
332
- assert !last_response.body.include?('job_with_params'), msg
333
- end
334
-
335
- test 'removes job from redis' do
336
- delete '/schedule', job_name: 'job_with_params'
337
-
338
- msg = 'The job was not deleted from redis.'
339
- assert_nil Resque.fetch_schedule('job_with_params'), msg
340
- end
341
- end
342
-
343
- context 'DELETE /schedule when static' do
344
- setup do
345
- Resque.schedule = Test::RESQUE_SCHEDULE
346
- Resque::Scheduler.load_schedule!
347
- Resque::Scheduler.stubs(:dynamic).returns(false)
348
- end
349
-
350
- test 'does not remove the job from the UI' do
351
- delete '/schedule', job_name: 'job_with_params'
352
- follow_redirect!
353
-
354
- msg = 'The job should not have been removed from the /schedule page.'
355
- assert last_response.body.include?('job_with_params'), msg
356
- end
357
-
358
- test 'does not remove job from redis' do
359
- delete '/schedule', job_name: 'job_with_params'
360
-
361
- msg = 'The job should not have been deleted from redis.'
362
- assert Resque.fetch_schedule('job_with_params'), msg
363
- end
364
- end