sidekiq-killswitch 1.0.0.pre1
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +679 -0
- data/CHANGES.md +2 -0
- data/Gemfile +4 -0
- data/LICENSE +201 -0
- data/README.md +155 -0
- data/Rakefile +18 -0
- data/examples/killswitch-web-ui.png +0 -0
- data/examples/square-logo.png +0 -0
- data/lib/sidekiq/extensions/dead_set.rb +23 -0
- data/lib/sidekiq/killswitch/config.rb +37 -0
- data/lib/sidekiq/killswitch/middleware/client.rb +20 -0
- data/lib/sidekiq/killswitch/middleware/load_all.rb +19 -0
- data/lib/sidekiq/killswitch/middleware/server.rb +20 -0
- data/lib/sidekiq/killswitch/version.rb +7 -0
- data/lib/sidekiq/killswitch/web.rb +62 -0
- data/lib/sidekiq/killswitch.rb +92 -0
- data/sidekiq-killswitch.gemspec +25 -0
- data/spec/config_spec.rb +33 -0
- data/spec/dead_set_spec.rb +47 -0
- data/spec/killswitch_spec.rb +145 -0
- data/spec/middleware/client_spec.rb +25 -0
- data/spec/middleware/server_spec.rb +46 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/web_spec.rb +110 -0
- data/web/locales/en.yml +15 -0
- data/web/views/kill_switches.html.erb +75 -0
- metadata +148 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require File.expand_path('../lib/sidekiq/killswitch/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'sidekiq-killswitch'
|
6
|
+
gem.version = Sidekiq::Killswitch::VERSION
|
7
|
+
gem.summary = 'Cross-host Sidekiq worker killswitches'
|
8
|
+
gem.authors = ['Yuriy Naidyon']
|
9
|
+
gem.email = 'yurokle@gmail.com'
|
10
|
+
gem.license = 'Apache-2.0'
|
11
|
+
gem.homepage = 'https://github.com/square/sidekiq-killswitch'
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($RS)
|
14
|
+
gem.test_files = gem.files.grep(%r{spec/})
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
gem.required_ruby_version = '>= 2.2.2'
|
17
|
+
gem.metadata['allowed_push_host'] = 'https://rubygems.org'
|
18
|
+
|
19
|
+
gem.add_runtime_dependency 'sidekiq', '>= 3'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rspec', '~> 3.6'
|
22
|
+
gem.add_development_dependency 'rack-test'
|
23
|
+
gem.add_development_dependency 'rspec-html-matchers'
|
24
|
+
gem.add_development_dependency 'rubocop', '~> 0.49.1'
|
25
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Sidekiq::Killswitch::Config do
|
5
|
+
let(:config) { Sidekiq::Killswitch::Config.new }
|
6
|
+
|
7
|
+
describe '#web_ui_worker_validator' do
|
8
|
+
it 'should to string presence validation by default' do
|
9
|
+
expect(config.web_ui_worker_validator.call(nil)).to be_falsey
|
10
|
+
expect(config.web_ui_worker_validator.call('')).to be_falsey
|
11
|
+
expect(config.web_ui_worker_validator.call('MyWorker')).to be_truthy
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#validate_worker_class_in_web' do
|
16
|
+
it 'should set Sidekiq::Worker module inclusion check as validator' do
|
17
|
+
stub_const('GoodWorker', Class.new {
|
18
|
+
include Sidekiq::Worker
|
19
|
+
})
|
20
|
+
stub_const('AlsoGoodWorker', Class.new(GoodWorker) {})
|
21
|
+
stub_const('BadWorker', Class.new {})
|
22
|
+
|
23
|
+
config.validate_worker_class_in_web
|
24
|
+
|
25
|
+
expect(config.web_ui_worker_validator.call('GoodWorker')).to be_truthy
|
26
|
+
expect(config.web_ui_worker_validator.call('AlsoGoodWorker')).to be_truthy
|
27
|
+
|
28
|
+
expect(config.web_ui_worker_validator.call('BadWorker')).to be_falsey
|
29
|
+
expect(config.web_ui_worker_validator.call('')).to be_falsey
|
30
|
+
expect(config.web_ui_worker_validator.call('Sidekiq::Worker')).to be_falsey
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Sidekiq::DeadSet do
|
5
|
+
let(:dead_set) { Sidekiq::DeadSet.new }
|
6
|
+
|
7
|
+
describe '#kill' do
|
8
|
+
it 'should put passed serialized job to the "dead" sorted set' do
|
9
|
+
serialized_job = Sidekiq.dump_json(jid: '123123', class: 'SomeWorker', args: [])
|
10
|
+
dead_set.kill(serialized_job)
|
11
|
+
|
12
|
+
expect(dead_set.find_job('123123').value).to eq(serialized_job)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should remove dead jobs older than Sidekiq::DeadSet.timeout' do
|
16
|
+
allow(Sidekiq::DeadSet).to receive(:timeout).and_return(10)
|
17
|
+
time_now = Time.now
|
18
|
+
|
19
|
+
stub_time_now(time_now - 11)
|
20
|
+
dead_set.kill(Sidekiq.dump_json({jid: '000103', class: 'MyWorker3', args: []})) # the oldest
|
21
|
+
|
22
|
+
stub_time_now(time_now - 9)
|
23
|
+
dead_set.kill(Sidekiq.dump_json({jid: '000102', class: 'MyWorker2', args: []}))
|
24
|
+
|
25
|
+
stub_time_now(time_now)
|
26
|
+
dead_set.kill(Sidekiq.dump_json({jid: '000101', class: 'MyWorker1', args: []}))
|
27
|
+
|
28
|
+
stub_time_now(time_now)
|
29
|
+
|
30
|
+
expect(dead_set.find_job('000103')).to be_falsey
|
31
|
+
expect(dead_set.find_job('000102')).to be_truthy
|
32
|
+
expect(dead_set.find_job('000101')).to be_truthy
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should remove all but last Sidekiq::DeadSet.max_jobs-1 jobs' do
|
36
|
+
allow(Sidekiq::DeadSet).to receive(:max_jobs).and_return(3)
|
37
|
+
|
38
|
+
dead_set.kill(Sidekiq.dump_json({jid: '000101', class: 'MyWorker1', args: []}))
|
39
|
+
dead_set.kill(Sidekiq.dump_json({jid: '000102', class: 'MyWorker2', args: []}))
|
40
|
+
dead_set.kill(Sidekiq.dump_json({jid: '000103', class: 'MyWorker3', args: []}))
|
41
|
+
|
42
|
+
expect(dead_set.find_job('000101')).to be_falsey
|
43
|
+
expect(dead_set.find_job('000102')).to be_truthy
|
44
|
+
expect(dead_set.find_job('000103')).to be_truthy
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Sidekiq::Killswitch do
|
5
|
+
# !!!!! HERE BE DRAGONS !!!!!
|
6
|
+
# Make sure to return test config to the working state after testing ...
|
7
|
+
# ... consider using Rspec.around wrapper:
|
8
|
+
#
|
9
|
+
# around do |example|
|
10
|
+
# original_test_logger = Sidekiq::Killswitch.logger
|
11
|
+
# example.run
|
12
|
+
# Sidekiq::Killswitch.logger = original_test_logger
|
13
|
+
# end
|
14
|
+
|
15
|
+
around do |example|
|
16
|
+
original_test_logger = Sidekiq::Killswitch.config.logger
|
17
|
+
|
18
|
+
example.run
|
19
|
+
|
20
|
+
Sidekiq::Killswitch.config.logger = original_test_logger
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:worker_name) { 'SomeWorker' }
|
24
|
+
|
25
|
+
describe '.configure' do
|
26
|
+
describe '.logger= ' do
|
27
|
+
it 'should allow to set the logger' do
|
28
|
+
logger = double
|
29
|
+
|
30
|
+
Sidekiq::Killswitch.configure do |config|
|
31
|
+
config.logger = logger
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(Sidekiq::Killswitch.logger).to eq(logger)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.blackhole_add_worker' do
|
40
|
+
it 'should mark a worker as blackholed in Redis' do
|
41
|
+
time_now = stub_time_now
|
42
|
+
|
43
|
+
Sidekiq::Killswitch.blackhole_add_worker(worker_name)
|
44
|
+
|
45
|
+
Sidekiq::Killswitch.redis_pool do |redis|
|
46
|
+
expect(redis.hget(Sidekiq::Killswitch::BLACKHOLE_WORKERS_KEY_NAME, worker_name)).to eq(time_now.to_s)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should accept class object as a parameter' do
|
51
|
+
time_now = stub_time_now
|
52
|
+
stub_const('AnotherWorker', Class.new {})
|
53
|
+
|
54
|
+
Sidekiq::Killswitch.blackhole_add_worker(AnotherWorker)
|
55
|
+
|
56
|
+
Sidekiq::Killswitch.redis_pool do |redis|
|
57
|
+
expect(redis.hget(Sidekiq::Killswitch::BLACKHOLE_WORKERS_KEY_NAME, 'AnotherWorker')).to eq(time_now.to_s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.blackhole_remove_worker' do
|
63
|
+
it 'should remove worker from the list of blackholed workers' do
|
64
|
+
Sidekiq::Killswitch.blackhole_add_worker(worker_name)
|
65
|
+
Sidekiq::Killswitch.blackhole_remove_worker(worker_name)
|
66
|
+
|
67
|
+
Sidekiq::Killswitch.redis_pool do |redis|
|
68
|
+
expect(redis.hexists(Sidekiq::Killswitch::BLACKHOLE_WORKERS_KEY_NAME, worker_name)).to be_falsey
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '.blackhole_worker?' do
|
74
|
+
it 'should return true for blackholed workers' do
|
75
|
+
Sidekiq::Killswitch.blackhole_add_worker(worker_name)
|
76
|
+
expect(Sidekiq::Killswitch.blackhole_worker?(worker_name)).to be_truthy
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should return false for non-blackholed workers' do
|
80
|
+
expect(Sidekiq::Killswitch.blackhole_worker?(worker_name)).to be_falsey
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.blackhole_workers' do
|
85
|
+
it 'should return a list of all blackholed workers with "added at" timestamps' do
|
86
|
+
first_worker_added_at = stub_time_now
|
87
|
+
Sidekiq::Killswitch.blackhole_add_worker('FirstWorker')
|
88
|
+
second_worker_added_at = stub_time_now(first_worker_added_at + 1)
|
89
|
+
Sidekiq::Killswitch.blackhole_add_worker('SecondWorker')
|
90
|
+
|
91
|
+
expect(Sidekiq::Killswitch.blackhole_workers).to eq({
|
92
|
+
'FirstWorker' => first_worker_added_at.to_s,
|
93
|
+
'SecondWorker' => second_worker_added_at.to_s
|
94
|
+
})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '.dead_queue_add_worker' do
|
99
|
+
it 'should mark a worker as a "dead queue worker" in Redis' do
|
100
|
+
time_now = stub_time_now
|
101
|
+
|
102
|
+
Sidekiq::Killswitch.dead_queue_add_worker(worker_name)
|
103
|
+
|
104
|
+
Sidekiq::Killswitch.redis_pool do |redis|
|
105
|
+
expect(redis.hget(Sidekiq::Killswitch::DEAD_QUEUE_WORKERS_KEY_NAME, worker_name)).to eq(time_now.to_s)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '.dead_queue_remove_worker' do
|
111
|
+
it 'should remove worker from the list of dead queue workers' do
|
112
|
+
Sidekiq::Killswitch.dead_queue_add_worker(worker_name)
|
113
|
+
Sidekiq::Killswitch.dead_queue_remove_worker(worker_name)
|
114
|
+
|
115
|
+
Sidekiq::Killswitch.redis_pool do |redis|
|
116
|
+
expect(redis.hexists(Sidekiq::Killswitch::DEAD_QUEUE_WORKERS_KEY_NAME, worker_name)).to be_falsey
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '.dead_queue_worker?' do
|
122
|
+
it 'should return true for dead queue workers' do
|
123
|
+
Sidekiq::Killswitch.dead_queue_add_worker(worker_name)
|
124
|
+
expect(Sidekiq::Killswitch.dead_queue_worker?(worker_name)).to be_truthy
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should return false for non-dead-queue workers' do
|
128
|
+
expect(Sidekiq::Killswitch.dead_queue_worker?(worker_name)).to be_falsey
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '.dead_queue_workers' do
|
133
|
+
it 'should return a list of all dead queue workers with "added at" timestamps' do
|
134
|
+
first_worker_added_at = stub_time_now
|
135
|
+
Sidekiq::Killswitch.dead_queue_add_worker('FirstWorker')
|
136
|
+
second_worker_added_at = stub_time_now(first_worker_added_at + 1)
|
137
|
+
Sidekiq::Killswitch.dead_queue_add_worker('SecondWorker')
|
138
|
+
|
139
|
+
expect(Sidekiq::Killswitch.dead_queue_workers).to eq({
|
140
|
+
'FirstWorker' => first_worker_added_at.to_s,
|
141
|
+
'SecondWorker' => second_worker_added_at.to_s
|
142
|
+
})
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Sidekiq::Killswitch::Middleware::Client do
|
5
|
+
let(:client_middleware) { Sidekiq::Killswitch::Middleware::Client.new }
|
6
|
+
|
7
|
+
describe '#call' do
|
8
|
+
it 'should return false for blackholed workers' do
|
9
|
+
stub_const('DisabledWorker', Class.new {})
|
10
|
+
|
11
|
+
Sidekiq::Killswitch.blackhole_add_worker(DisabledWorker)
|
12
|
+
Sidekiq::Killswitch.blackhole_add_worker('MyWorker')
|
13
|
+
|
14
|
+
expect(client_middleware.call(DisabledWorker, {}, nil, nil) { 123 }).to eq(false)
|
15
|
+
expect(client_middleware.call('MyWorker', {}, nil, nil) { 123 }).to eq(false)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should return block result for not blockholed workers' do
|
19
|
+
stub_const('EnabledWorker', Class.new {})
|
20
|
+
|
21
|
+
expect(client_middleware.call(EnabledWorker, {}, nil, nil) { 'result' }).to eq('result')
|
22
|
+
expect(client_middleware.call('EnabledWorker', {}, nil, nil) { 123 }).to eq(123)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Sidekiq::Killswitch::Middleware::Server do
|
5
|
+
let(:server_middleware) { Sidekiq::Killswitch::Middleware::Server.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
stub_const('MyWorker', Class.new {
|
9
|
+
include Sidekiq::Worker
|
10
|
+
def perform; end
|
11
|
+
})
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#call' do
|
15
|
+
context 'for blackholed workers' do
|
16
|
+
it 'should not run block' do
|
17
|
+
Sidekiq::Killswitch.blackhole_add_worker(MyWorker)
|
18
|
+
|
19
|
+
expect do
|
20
|
+
server_middleware.call(MyWorker.new, {}, nil) { raise 'Should not run' }
|
21
|
+
end.to_not raise_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'for dead-queued workers' do
|
26
|
+
it 'should send a job to the morgue' do
|
27
|
+
Sidekiq::Killswitch.dead_queue_add_worker(MyWorker)
|
28
|
+
|
29
|
+
job_data = double
|
30
|
+
expect_any_instance_of(Sidekiq::DeadSet).to receive(:kill).with(job_data)
|
31
|
+
|
32
|
+
expect do
|
33
|
+
server_middleware.call(MyWorker.new, job_data, nil) { raise 'Should not run' }
|
34
|
+
end.to_not raise_error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'for non-marked workers' do
|
39
|
+
it 'should run passed block' do
|
40
|
+
checkpoint = double
|
41
|
+
expect(checkpoint).to receive(:check)
|
42
|
+
server_middleware.call(MyWorker.new, {}, nil) { checkpoint.check }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rspec'
|
3
|
+
require 'logger'
|
4
|
+
require 'sidekiq/testing'
|
5
|
+
require 'sidekiq/killswitch'
|
6
|
+
|
7
|
+
ENV['RACK_ENV'] = 'test' # Disable CSRF protection for Sidekiq Web app
|
8
|
+
|
9
|
+
Sidekiq::Killswitch.configure do |config|
|
10
|
+
config.logger = Logger.new('/dev/null')
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.disable_monkey_patching!
|
15
|
+
config.before do
|
16
|
+
Sidekiq::Killswitch.redis_pool { |c| c.flushdb }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def stub_time_now(time_now = Time.now)
|
21
|
+
allow(Time).to receive(:now).and_return(time_now)
|
22
|
+
time_now
|
23
|
+
end
|
data/spec/web_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rack/test'
|
4
|
+
require 'rspec-html-matchers'
|
5
|
+
require 'sidekiq/killswitch/web'
|
6
|
+
|
7
|
+
RSpec.describe Sidekiq::Killswitch::Web do
|
8
|
+
include Rack::Test::Methods
|
9
|
+
include RSpecHtmlMatchers
|
10
|
+
|
11
|
+
let(:app) { Sidekiq::Web }
|
12
|
+
|
13
|
+
def expect_redirect_to_root_page(response)
|
14
|
+
expect(response.status).to be(302)
|
15
|
+
expect(response.headers['Location']).to eq("http://#{rack_mock_session.default_host}/kill-switches")
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'GET /kill-switches' do
|
19
|
+
it 'should list blackholed workers' do
|
20
|
+
Sidekiq::Killswitch.blackhole_add_worker('WorkerOne')
|
21
|
+
Sidekiq::Killswitch.blackhole_add_worker('WorkerTwo')
|
22
|
+
|
23
|
+
response = get('/kill-switches')
|
24
|
+
expect(response.status).to be(200)
|
25
|
+
|
26
|
+
expect(response.body).to have_tag('.blackhole-workers') do
|
27
|
+
with_text('WorkerOne')
|
28
|
+
with_text('WorkerTwo')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should list dead-queued workers' do
|
33
|
+
Sidekiq::Killswitch.dead_queue_add_worker('DeadWorkerOne')
|
34
|
+
Sidekiq::Killswitch.dead_queue_add_worker('DeadWorkerTwo')
|
35
|
+
|
36
|
+
response = get('/kill-switches')
|
37
|
+
|
38
|
+
expect(response.body).to have_tag('.dead-queue-workers') do
|
39
|
+
with_text('DeadWorkerOne')
|
40
|
+
with_text('DeadWorkerTwo')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'POST /kill-switches/blackhole_add' do
|
46
|
+
it 'should blackhole passed worker' do
|
47
|
+
response = post('/kill-switches/blackhole_add', worker_name: 'BlackholedWorker')
|
48
|
+
|
49
|
+
expect_redirect_to_root_page(response)
|
50
|
+
expect(Sidekiq::Killswitch.blackhole_worker?('BlackholedWorker')).to be_truthy
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'validation' do
|
54
|
+
around do |example|
|
55
|
+
default_validator = Sidekiq::Killswitch.config.web_ui_worker_validator
|
56
|
+
example.run
|
57
|
+
Sidekiq::Killswitch.config.web_ui_worker_validator = default_validator
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should perform basic default validation' do
|
61
|
+
post('/kill-switches/blackhole_add', worker_name: '')
|
62
|
+
follow_redirect!
|
63
|
+
|
64
|
+
expect(last_response.body).to have_tag('.error-message', text: 'Error: Invalid worker name!')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should perform custom validation' do
|
68
|
+
Sidekiq::Killswitch.config.web_ui_worker_validator = ->(name) { name.end_with?('BlockedWorker') }
|
69
|
+
|
70
|
+
post('/kill-switches/blackhole_add', worker_name: 'BadWorker')
|
71
|
+
follow_redirect!
|
72
|
+
expect(last_response.body).to have_tag('.error-message', text: 'Error: Invalid worker name!')
|
73
|
+
|
74
|
+
post('/kill-switches/blackhole_add', worker_name: 'MyBlockedWorker')
|
75
|
+
expect(Sidekiq::Killswitch.blackhole_worker?('MyBlockedWorker')).to be_truthy
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'POST /kill-switches/blackhole_remove' do
|
81
|
+
it 'should remove worker from blackhole' do
|
82
|
+
Sidekiq::Killswitch.blackhole_add_worker('BlakcholedWorker')
|
83
|
+
|
84
|
+
response = post('/kill-switches/blackhole_remove', worker_name: 'BlackholedWorker')
|
85
|
+
|
86
|
+
expect_redirect_to_root_page(response)
|
87
|
+
expect(Sidekiq::Killswitch.blackhole_worker?('BlackholedWorker')).to be_falsey
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'POST /kill-switches/dead_queue_add' do
|
92
|
+
it 'should add passed worker to dead queue' do
|
93
|
+
response = post('/kill-switches/dead_queue_add', worker_name: 'DeadWorker')
|
94
|
+
|
95
|
+
expect_redirect_to_root_page(response)
|
96
|
+
expect(Sidekiq::Killswitch.dead_queue_worker?('DeadWorker')).to be_truthy
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'POST /kill-switches/dead_queue_remove' do
|
101
|
+
it 'should remove worker from dead queue' do
|
102
|
+
Sidekiq::Killswitch.dead_queue_add_worker('DeadWorker')
|
103
|
+
|
104
|
+
response = post('/kill-switches/dead_queue_remove', worker_name: 'DeadWorker')
|
105
|
+
|
106
|
+
expect_redirect_to_root_page(response)
|
107
|
+
expect(Sidekiq::Killswitch.dead_queue_worker?('DeadWorker')).to be_falsey
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/web/locales/en.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
en:
|
2
|
+
kill_switches: Kill Switches
|
3
|
+
blackholed_workers: Blackholed Workers
|
4
|
+
dead_queue_workers: Dead Queue Workers
|
5
|
+
are_you_sure: Are you sure?
|
6
|
+
blackhole: Blackhole
|
7
|
+
disabled_at: Disabled at
|
8
|
+
dead_queued_at: Dead-queued at
|
9
|
+
blackholed_at: Blackholed at
|
10
|
+
send_to_dead_queue: Send to dead queue
|
11
|
+
class: Class
|
12
|
+
kill: Kill
|
13
|
+
resurrect: Resurrect
|
14
|
+
resurrect_worker: Resurrect %{worker}?
|
15
|
+
invalid_worker_error: "Error: Invalid worker name!"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
<% if @worker_name_invalid %>
|
2
|
+
<h4 class="error-message alert alert-danger"><%= t('invalid_worker_error')%></h4>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<h3><%= t('blackholed_workers')%></h3>
|
6
|
+
|
7
|
+
<form action="<%= root_path %>kill-switches/blackhole_add" method="post" style="margin-bottom: 4px;">
|
8
|
+
<%= csrf_tag %>
|
9
|
+
<input name="worker_name" />
|
10
|
+
<input class="btn btn-danger btn-xs" type="submit" value="<%= t('blackhole')%>" data-confirm="<%= t('are_you_sure')%>" />
|
11
|
+
</form>
|
12
|
+
|
13
|
+
<div class="table_container">
|
14
|
+
<table class="blackhole-workers table table-hover table-bordered table-striped table-white">
|
15
|
+
<thead>
|
16
|
+
<tr>
|
17
|
+
<th><%= t('class')%></th>
|
18
|
+
<th style="width: 100%;"><%= t('blackholed_at')%></th>
|
19
|
+
<th></th>
|
20
|
+
</tr>
|
21
|
+
</thead>
|
22
|
+
|
23
|
+
<tbody>
|
24
|
+
<% @blackhole_workers.each do |worker_name, disabled_at| %>
|
25
|
+
<tr>
|
26
|
+
<td><%= worker_name %></td>
|
27
|
+
<td><%= disabled_at %></td>
|
28
|
+
<td class="text-center">
|
29
|
+
<form action="<%= root_path %>kill-switches/blackhole_remove" method="post">
|
30
|
+
<%= csrf_tag %>
|
31
|
+
<input type="hidden" name="worker_name" value="<%= worker_name %>" />
|
32
|
+
<input class="btn btn-primary btn-xs pull-right" type="submit" value="<%= t('restore')%>" data-confirm="<%= t('restore_worker', worker: worker_name) %>" />
|
33
|
+
</form>
|
34
|
+
</td>
|
35
|
+
</tr>
|
36
|
+
<% end %>
|
37
|
+
</tbody>
|
38
|
+
</table>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<h3><%= t('dead_queue_workers') %></h3>
|
42
|
+
|
43
|
+
<form action="<%= root_path %>kill-switches/dead_queue_add" method="post" style="margin-bottom: 4px;">
|
44
|
+
<%= csrf_tag %>
|
45
|
+
<input name="worker_name" />
|
46
|
+
<input class="btn btn-danger btn-xs" type="submit" value="<%= t('send_to_dead_queue') %>" data-confirm="<%= t('are_you_sure')%>" />
|
47
|
+
</form>
|
48
|
+
|
49
|
+
<div class="table_container">
|
50
|
+
<table class="dead-queue-workers table table-hover table-bordered table-striped table-white">
|
51
|
+
<thead>
|
52
|
+
<tr>
|
53
|
+
<th><%= t('class')%></th>
|
54
|
+
<th style="width: 100%;"><%= t('dead_queued_at')%></th>
|
55
|
+
<th></th>
|
56
|
+
</tr>
|
57
|
+
</thead>
|
58
|
+
|
59
|
+
<tbody>
|
60
|
+
<% @dead_queue_workers.each do |worker_name, disabled_at| %>
|
61
|
+
<tr>
|
62
|
+
<td><%= worker_name %></td>
|
63
|
+
<td><%= disabled_at %></td>
|
64
|
+
<td class="text-center">
|
65
|
+
<form action="<%= root_path %>kill-switches/dead_queue_remove" method="post">
|
66
|
+
<%= csrf_tag %>
|
67
|
+
<input type="hidden" name="worker_name" value="<%= worker_name %>" />
|
68
|
+
<input class="btn btn-primary btn-xs pull-right" type="submit" value="<%= t('resurrect')%>" data-confirm="<%= t('resurrect_worker', worker: worker_name) %>" />
|
69
|
+
</form>
|
70
|
+
</td>
|
71
|
+
</tr>
|
72
|
+
<% end %>
|
73
|
+
</tbody>
|
74
|
+
</table>
|
75
|
+
</div>
|