sidekiq-status 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +4 -1
- data/Appraisals +11 -0
- data/CHANGELOG.md +18 -7
- data/README.md +99 -44
- data/Rakefile +2 -0
- data/gemfiles/sidekiq_3.x.gemfile +7 -0
- data/gemfiles/sidekiq_4.x.gemfile +7 -0
- data/gemfiles/sidekiq_5.x.gemfile +7 -0
- data/lib/sidekiq-status.rb +4 -3
- data/lib/sidekiq-status/client_middleware.rb +28 -7
- data/lib/sidekiq-status/server_middleware.rb +63 -16
- data/lib/sidekiq-status/sidekiq_extensions.rb +7 -0
- data/lib/sidekiq-status/testing/inline.rb +4 -0
- data/lib/sidekiq-status/version.rb +1 -1
- data/lib/sidekiq-status/web.rb +68 -24
- data/lib/sidekiq-status/worker.rb +4 -2
- data/sidekiq-status.gemspec +5 -3
- data/spec/lib/sidekiq-status/client_middleware_spec.rb +16 -10
- data/spec/lib/sidekiq-status/server_middleware_spec.rb +24 -10
- data/spec/lib/sidekiq-status/web_spec.rb +1 -1
- data/spec/lib/sidekiq-status/worker_spec.rb +1 -1
- data/spec/lib/sidekiq-status_spec.rb +40 -12
- data/spec/spec_helper.rb +54 -22
- data/spec/support/test_jobs.rb +17 -3
- data/web/views/status.erb +10 -10
- data/web/views/statuses.erb +75 -16
- metadata +40 -5
@@ -15,11 +15,12 @@ describe Sidekiq::Status do
|
|
15
15
|
|
16
16
|
start_server do
|
17
17
|
expect(capture_status_updates(2) {
|
18
|
-
expect(LongJob.perform_async(
|
18
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
19
19
|
}).to eq([job_id]*2)
|
20
20
|
expect(Sidekiq::Status.status(job_id)).to eq(:working)
|
21
21
|
expect(Sidekiq::Status.working?(job_id)).to be_truthy
|
22
22
|
expect(Sidekiq::Status::queued?(job_id)).to be_falsey
|
23
|
+
expect(Sidekiq::Status::retrying?(job_id)).to be_falsey
|
23
24
|
expect(Sidekiq::Status::failed?(job_id)).to be_falsey
|
24
25
|
expect(Sidekiq::Status::complete?(job_id)).to be_falsey
|
25
26
|
expect(Sidekiq::Status::stopped?(job_id)).to be_falsey
|
@@ -49,9 +50,9 @@ describe Sidekiq::Status do
|
|
49
50
|
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
50
51
|
|
51
52
|
start_server do
|
52
|
-
expect(capture_status_updates(
|
53
|
+
expect(capture_status_updates(4) {
|
53
54
|
expect(ProgressJob.perform_async).to eq(job_id)
|
54
|
-
}).to eq([job_id]*
|
55
|
+
}).to eq([job_id]*4)
|
55
56
|
end
|
56
57
|
expect(Sidekiq::Status.at(job_id)).to be(100)
|
57
58
|
expect(Sidekiq::Status.total(job_id)).to be(500)
|
@@ -67,7 +68,7 @@ describe Sidekiq::Status do
|
|
67
68
|
|
68
69
|
start_server do
|
69
70
|
expect(capture_status_updates(2) {
|
70
|
-
expect(LongJob.perform_async(
|
71
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
71
72
|
}).to eq([job_id]*2)
|
72
73
|
expect(hash = Sidekiq::Status.get_all(job_id)).to include 'status' => 'working'
|
73
74
|
expect(hash).to include 'update_time'
|
@@ -82,7 +83,7 @@ describe Sidekiq::Status do
|
|
82
83
|
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
83
84
|
start_server do
|
84
85
|
expect(capture_status_updates(2) {
|
85
|
-
expect(LongJob.perform_async(
|
86
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
86
87
|
}).to eq([job_id]*2)
|
87
88
|
end
|
88
89
|
expect(Sidekiq::Status.delete(job_id)).to eq(1)
|
@@ -147,13 +148,30 @@ describe Sidekiq::Status do
|
|
147
148
|
end
|
148
149
|
|
149
150
|
it "retries failed jobs" do
|
150
|
-
allow(SecureRandom).to receive(:hex).
|
151
|
+
allow(SecureRandom).to receive(:hex).and_return(retried_job_id)
|
151
152
|
start_server do
|
152
|
-
expect(capture_status_updates(
|
153
|
+
expect(capture_status_updates(3) {
|
153
154
|
expect(RetriedJob.perform_async()).to eq(retried_job_id)
|
154
|
-
}).to eq([retried_job_id] *
|
155
|
+
}).to eq([retried_job_id] * 3)
|
156
|
+
expect(Sidekiq::Status.status(retried_job_id)).to eq(:retrying)
|
157
|
+
expect(Sidekiq::Status.working?(retried_job_id)).to be_falsey
|
158
|
+
expect(Sidekiq::Status::queued?(retried_job_id)).to be_falsey
|
159
|
+
expect(Sidekiq::Status::retrying?(retried_job_id)).to be_truthy
|
160
|
+
expect(Sidekiq::Status::failed?(retried_job_id)).to be_falsey
|
161
|
+
expect(Sidekiq::Status::complete?(retried_job_id)).to be_falsey
|
162
|
+
expect(Sidekiq::Status::stopped?(retried_job_id)).to be_falsey
|
163
|
+
expect(Sidekiq::Status::interrupted?(retried_job_id)).to be_falsey
|
164
|
+
end
|
165
|
+
expect(Sidekiq::Status.status(retried_job_id)).to eq(:retrying)
|
166
|
+
expect(Sidekiq::Status::retrying?(retried_job_id)).to be_truthy
|
167
|
+
|
168
|
+
# restarting and waiting for the job to complete
|
169
|
+
start_server do
|
170
|
+
expect(capture_status_updates(3) {}).to eq([retried_job_id] * 3)
|
171
|
+
expect(Sidekiq::Status.status(retried_job_id)).to eq(:complete)
|
172
|
+
expect(Sidekiq::Status.complete?(retried_job_id)).to be_truthy
|
173
|
+
expect(Sidekiq::Status::retrying?(retried_job_id)).to be_falsey
|
155
174
|
end
|
156
|
-
expect(Sidekiq::Status.status(retried_job_id)).to eq(:complete)
|
157
175
|
end
|
158
176
|
|
159
177
|
context ":expiration param" do
|
@@ -166,7 +184,7 @@ describe Sidekiq::Status do
|
|
166
184
|
expect_2_jobs_ttl_covers (Sidekiq::Status::DEFAULT_EXPIRY+1)..expiration_param
|
167
185
|
end
|
168
186
|
|
169
|
-
it "allow to overwrite :expiration parameter by
|
187
|
+
it "allow to overwrite :expiration parameter by #expiration method from worker" do
|
170
188
|
overwritten_expiration = expiration_param * 100
|
171
189
|
allow_any_instance_of(NoStatusConfirmationJob).to receive(:expiration).
|
172
190
|
and_return(overwritten_expiration)
|
@@ -176,6 +194,16 @@ describe Sidekiq::Status do
|
|
176
194
|
expect_2_jobs_are_done_and_status_eq :complete
|
177
195
|
expect_2_jobs_ttl_covers (expiration_param+1)..overwritten_expiration
|
178
196
|
end
|
197
|
+
|
198
|
+
it "reads #expiration from a method when defined" do
|
199
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id, job_id_1)
|
200
|
+
start_server do
|
201
|
+
expect(StubJob.perform_async).to eq(job_id)
|
202
|
+
expect(ExpiryJob.perform_async).to eq(job_id_1)
|
203
|
+
expect(redis.ttl("sidekiq:status:#{job_id}")).to eq(30 * 60)
|
204
|
+
expect(redis.ttl("sidekiq:status:#{job_id_1}")).to eq(15)
|
205
|
+
end
|
206
|
+
end
|
179
207
|
end
|
180
208
|
|
181
209
|
def seed_secure_random_with_job_ids
|
@@ -185,12 +213,12 @@ describe Sidekiq::Status do
|
|
185
213
|
|
186
214
|
def run_2_jobs!
|
187
215
|
start_server(:expiration => expiration_param) do
|
188
|
-
expect(capture_status_updates(
|
216
|
+
expect(capture_status_updates(6) {
|
189
217
|
expect(StubJob.perform_async).to eq(plain_sidekiq_job_id)
|
190
218
|
NoStatusConfirmationJob.perform_async(1)
|
191
219
|
expect(StubJob.perform_async).to eq(job_id_1)
|
192
220
|
NoStatusConfirmationJob.perform_async(2)
|
193
|
-
}).to match_array([plain_sidekiq_job_id, job_id_1] *
|
221
|
+
}).to match_array([plain_sidekiq_job_id, job_id_1] * 3)
|
194
222
|
end
|
195
223
|
end
|
196
224
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require "rspec"
|
2
|
-
|
2
|
+
require 'colorize'
|
3
3
|
require 'sidekiq'
|
4
|
+
|
5
|
+
# Celluloid should only be manually required before Sidekiq versions 4.+
|
6
|
+
require 'sidekiq/version'
|
7
|
+
require 'celluloid' if Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new('4.0')
|
8
|
+
|
4
9
|
require 'sidekiq/processor'
|
5
10
|
require 'sidekiq/manager'
|
6
11
|
require 'sidekiq-status'
|
@@ -9,71 +14,98 @@ require 'sidekiq-status'
|
|
9
14
|
RSpec.configure do |config|
|
10
15
|
config.before(:each) do
|
11
16
|
Sidekiq.redis { |conn| conn.flushall }
|
17
|
+
client_middleware
|
12
18
|
sleep 0.05
|
13
19
|
end
|
14
20
|
end
|
15
21
|
|
16
22
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
17
23
|
|
18
|
-
|
24
|
+
# Configures client middleware
|
25
|
+
def client_middleware client_middleware_options = {}
|
19
26
|
Sidekiq.configure_client do |config|
|
20
|
-
|
21
|
-
chain.add Sidekiq::Status::ClientMiddleware, client_middleware_options
|
22
|
-
end
|
27
|
+
Sidekiq::Status.configure_client_middleware config, client_middleware_options
|
23
28
|
end
|
24
29
|
end
|
25
30
|
|
26
|
-
def
|
31
|
+
def redis_thread messages_limit, *channels
|
32
|
+
|
27
33
|
parent = Thread.current
|
28
34
|
thread = Thread.new {
|
29
|
-
|
35
|
+
messages = []
|
30
36
|
Sidekiq.redis do |conn|
|
31
|
-
|
37
|
+
puts "Subscribing to #{channels} for #{messages_limit.to_s.bold} messages".cyan if ENV['DEBUG']
|
38
|
+
conn.subscribe_with_timeout 30, *channels do |on|
|
32
39
|
on.subscribe do |ch, subscriptions|
|
40
|
+
puts "Subscribed to #{ch}".cyan if ENV['DEBUG']
|
33
41
|
if subscriptions == channels.size
|
34
42
|
sleep 0.1 while parent.status != "sleep"
|
35
43
|
parent.run
|
36
44
|
end
|
37
45
|
end
|
38
46
|
on.message do |ch, msg|
|
39
|
-
|
40
|
-
|
47
|
+
puts "Message received: #{ch} -> #{msg}".white if ENV['DEBUG']
|
48
|
+
messages << msg
|
49
|
+
conn.unsubscribe if messages.length >= messages_limit
|
41
50
|
end
|
42
51
|
end
|
43
52
|
end
|
44
|
-
|
53
|
+
puts "Returing from thread".cyan if ENV['DEBUG']
|
54
|
+
messages
|
45
55
|
}
|
56
|
+
|
46
57
|
Thread.stop
|
47
58
|
yield if block_given?
|
48
59
|
thread
|
60
|
+
|
49
61
|
end
|
50
62
|
|
51
|
-
def capture_status_updates
|
52
|
-
|
63
|
+
def capture_status_updates n, &block
|
64
|
+
redis_thread(n, "status_updates", &block).value
|
53
65
|
end
|
54
66
|
|
55
|
-
|
67
|
+
# Configures server middleware and launches a sidekiq server
|
68
|
+
def start_server server_middleware_options = {}
|
69
|
+
|
70
|
+
# Creates a process for the Sidekiq server
|
56
71
|
pid = Process.fork do
|
57
|
-
|
58
|
-
|
72
|
+
|
73
|
+
# Redirect the server's outputs
|
74
|
+
$stdout.reopen File::NULL, 'w' unless ENV['DEBUG']
|
75
|
+
$stderr.reopen File::NULL, 'w' unless ENV['DEBUG']
|
76
|
+
|
77
|
+
# Load and configure server options
|
59
78
|
require 'sidekiq/cli'
|
60
79
|
Sidekiq.options[:queues] << 'default'
|
61
|
-
Sidekiq.options[:require] = File.expand_path
|
80
|
+
Sidekiq.options[:require] = File.expand_path 'environment.rb', File.dirname(__FILE__)
|
62
81
|
Sidekiq.options[:timeout] = 1
|
63
82
|
Sidekiq.options[:concurrency] = 5
|
83
|
+
|
84
|
+
# Add the server middleware
|
64
85
|
Sidekiq.configure_server do |config|
|
65
86
|
config.redis = Sidekiq::RedisConnection.create
|
66
|
-
|
67
|
-
chain.add Sidekiq::Status::ServerMiddleware, server_middleware_options
|
68
|
-
end
|
87
|
+
Sidekiq::Status.configure_server_middleware config, server_middleware_options
|
69
88
|
end
|
89
|
+
|
90
|
+
# Launch
|
91
|
+
puts "Server starting".yellow if ENV['DEBUG']
|
70
92
|
Sidekiq::CLI.instance.run
|
93
|
+
|
71
94
|
end
|
72
95
|
|
96
|
+
# Run the client-side code
|
73
97
|
yield
|
74
|
-
|
98
|
+
|
99
|
+
# Pause to ensure all jobs are picked up & started before TERM is sent
|
100
|
+
sleep 0.2
|
101
|
+
|
102
|
+
# Attempt to shut down the server normally
|
75
103
|
Process.kill 'TERM', pid
|
76
|
-
|
104
|
+
Process.wait pid
|
105
|
+
|
77
106
|
ensure
|
107
|
+
|
108
|
+
# Ensure the server is actually dead
|
78
109
|
Process.kill 'KILL', pid rescue "OK" # it's OK if the process is gone already
|
110
|
+
|
79
111
|
end
|
data/spec/support/test_jobs.rb
CHANGED
@@ -4,12 +4,18 @@ class StubJob
|
|
4
4
|
include Sidekiq::Worker
|
5
5
|
include Sidekiq::Status::Worker
|
6
6
|
|
7
|
-
sidekiq_options 'retry' =>
|
7
|
+
sidekiq_options 'retry' => false
|
8
8
|
|
9
9
|
def perform(*args)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
class ExpiryJob < StubJob
|
14
|
+
def expiration
|
15
|
+
15
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
13
19
|
class LongJob < StubJob
|
14
20
|
def perform(*args)
|
15
21
|
sleep args[0] || 0.25
|
@@ -56,6 +62,12 @@ class FailingJob < StubJob
|
|
56
62
|
end
|
57
63
|
end
|
58
64
|
|
65
|
+
class FailingHardJob < StubJob
|
66
|
+
def perform
|
67
|
+
raise Exception
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
59
71
|
class ExitedJob < StubJob
|
60
72
|
def perform
|
61
73
|
raise SystemExit
|
@@ -69,11 +81,13 @@ class InterruptedJob < StubJob
|
|
69
81
|
end
|
70
82
|
|
71
83
|
class RetriedJob < StubJob
|
72
|
-
|
84
|
+
|
85
|
+
sidekiq_options 'retry' => true
|
86
|
+
sidekiq_retry_in do |count| 3 end # 3 second delay > job timeout in test suite
|
87
|
+
|
73
88
|
def perform()
|
74
89
|
Sidekiq.redis do |conn|
|
75
90
|
key = "RetriedJob_#{jid}"
|
76
|
-
sleep 1
|
77
91
|
unless conn.exists key
|
78
92
|
conn.set key, 'tried'
|
79
93
|
raise StandardError
|
data/web/views/status.erb
CHANGED
@@ -14,30 +14,30 @@
|
|
14
14
|
</style>
|
15
15
|
|
16
16
|
<h3>
|
17
|
-
Job Status: <%= @status
|
18
|
-
<span class='label label-<%= @status
|
19
|
-
<%= @status
|
17
|
+
Job Status: <%= @status["jid"] %>
|
18
|
+
<span class='label label-<%= @status["label"] %>'>
|
19
|
+
<%= @status["status"] %>
|
20
20
|
</span>
|
21
21
|
</h3>
|
22
22
|
|
23
23
|
<div class="progress" style="height: 30px;">
|
24
|
-
<div class="progress-bar" role="progressbar" aria-valuenow="<%= @status
|
24
|
+
<div class="progress-bar" role="progressbar" aria-valuenow="<%= @status["pct_complete"].to_i %>" aria-valuemin="0" aria-valuemax="100" style="width: <%= @status["pct_complete"].to_i %>%">
|
25
25
|
<div class="progress-percentage">
|
26
|
-
<%= @status
|
26
|
+
<%= @status["pct_complete"].to_i %>%
|
27
27
|
</div>
|
28
28
|
</div>
|
29
29
|
</div>
|
30
30
|
|
31
31
|
<div class="panel panel-default">
|
32
32
|
<div class="panel-body">
|
33
|
-
<h4><%= @status
|
33
|
+
<h4><%= @status["worker"] %></h4>
|
34
34
|
|
35
35
|
<div class="row">
|
36
36
|
<div class="col-sm-2">
|
37
37
|
<strong>Arguments</strong>
|
38
38
|
</div>
|
39
39
|
<div class="col-sm-10">
|
40
|
-
<p><%= @status
|
40
|
+
<p><%= @status["args"].empty? ? "<i>none</i>" : @status["args"] %></p>
|
41
41
|
</div>
|
42
42
|
</div>
|
43
43
|
|
@@ -46,7 +46,7 @@
|
|
46
46
|
<strong>Message</strong>
|
47
47
|
</div>
|
48
48
|
<div class="col-sm-10">
|
49
|
-
<p><%= @status
|
49
|
+
<p><%= @status["message"] || "<i>none</i>" %></p>
|
50
50
|
</div>
|
51
51
|
</div>
|
52
52
|
|
@@ -56,9 +56,9 @@
|
|
56
56
|
</div>
|
57
57
|
<div class="col-sm-10">
|
58
58
|
<p>
|
59
|
-
<% secs = Time.now.to_i - @status
|
59
|
+
<% secs = Time.now.to_i - @status["update_time"].to_i %>
|
60
60
|
<% if secs > 0 %>
|
61
|
-
<%= secs
|
61
|
+
<%= ChronicDuration.output(secs, :weeks => true, :units => 2) %> ago
|
62
62
|
<% else %>
|
63
63
|
Now
|
64
64
|
<% end %>
|
data/web/views/statuses.erb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
<style>
|
3
2
|
.progress {
|
4
3
|
background-color: #C8E1ED;
|
@@ -13,19 +12,61 @@
|
|
13
12
|
font-weight: bold; padding-left: 4px;
|
14
13
|
color: #333;
|
15
14
|
}
|
16
|
-
.
|
15
|
+
.actions {
|
17
16
|
text-align: center;
|
18
17
|
}
|
19
|
-
.
|
18
|
+
.header {
|
19
|
+
text-align: center;
|
20
|
+
}
|
21
|
+
.header_update_time {
|
20
22
|
width: 10%;
|
21
23
|
}
|
22
|
-
.header_pct_complete{
|
24
|
+
.header_pct_complete {
|
23
25
|
width: 45%;
|
24
26
|
}
|
25
|
-
|
27
|
+
.btn-warning {
|
28
|
+
background-image: linear-gradient(#f0ad4e, #eea236)
|
29
|
+
}
|
30
|
+
.nav-container {
|
31
|
+
display: flex;
|
32
|
+
line-height: 45px;
|
33
|
+
}
|
34
|
+
.nav-container .pull-right {
|
35
|
+
float: none !important;
|
36
|
+
}
|
37
|
+
.nav-container .pagination {
|
38
|
+
display: flex;
|
39
|
+
align-items: center;
|
40
|
+
}
|
41
|
+
.nav-container .per-page {
|
42
|
+
display: flex;
|
43
|
+
align-items: center;
|
44
|
+
margin: 20px 0 20px 10px;
|
45
|
+
white-space: nowrap;
|
46
|
+
}
|
47
|
+
.nav-container .per-page SELECT {
|
48
|
+
margin: 0 0 0 5px;
|
49
|
+
}
|
26
50
|
</style>
|
27
|
-
|
28
|
-
|
51
|
+
<script>
|
52
|
+
function setPerPage(select){
|
53
|
+
window.location = select.options[select.selectedIndex].getAttribute('data-url')
|
54
|
+
}
|
55
|
+
</script>
|
56
|
+
<div style="display: flex; justify-content: space-between;">
|
57
|
+
<h3 class="wi">Recent job statuses</h3>
|
58
|
+
<div class="nav-container">
|
59
|
+
<%= erb :_paging, locals: { url: "#{root_path}statuses" } %>
|
60
|
+
<div class="per-page">
|
61
|
+
Per page:
|
62
|
+
<select class="form-control" onchange="setPerPage(this)">
|
63
|
+
<% (Sidekiq::Status::Web.per_page_opts + ['all']).each do |num| %>
|
64
|
+
<option data-url="?<%= qparams(page: 1, per_page: num)%>" value="<%= num %>" <%= 'selected="selected"' if num.to_s == (params[:per_page] || @count) %>><%= num %></option>
|
65
|
+
<% end %>
|
66
|
+
</select>
|
67
|
+
</div>
|
68
|
+
</div>
|
69
|
+
</div>
|
29
70
|
<table class="table table-hover table-bordered table-striped table-white">
|
30
71
|
<tr>
|
31
72
|
<% @headers.each do |h| %>
|
@@ -33,20 +74,23 @@
|
|
33
74
|
<a href="<%= h[:url] %>"><%= h[:name] %></a>
|
34
75
|
</th>
|
35
76
|
<% end %>
|
77
|
+
<th class="header">
|
78
|
+
Actions
|
79
|
+
</th>
|
36
80
|
</tr>
|
37
81
|
<% @statuses.each do |container| %>
|
38
82
|
<tr>
|
39
83
|
<td>
|
40
|
-
<div title='<%= container
|
84
|
+
<div title='<%= container["jid"] %>'><a href="<%= root_path %>statuses/<%= container["jid"] %>"><%= container["worker"] %></a></div>
|
41
85
|
</td>
|
42
86
|
<td>
|
43
|
-
<div class='args' title='<%= container
|
87
|
+
<div class='args' title='<%= container["jid"] %>'><%= container["args"] %></div>
|
44
88
|
</td>
|
45
89
|
<td style='text-align: center;'>
|
46
|
-
<div class='label label-<%= container
|
90
|
+
<div class='label label-<%= container["label"] %>'><%= container["status"] %></div>
|
47
91
|
</td>
|
48
|
-
<% secs = Time.now.to_i - container
|
49
|
-
<td style='text-align: center; white-space: nowrap;' title="<%= Time.at(container
|
92
|
+
<% secs = Time.now.to_i - container["update_time"].to_i %>
|
93
|
+
<td style='text-align: center; white-space: nowrap;' title="<%= Time.at(container["update_time"].to_i) %>">
|
50
94
|
<% if secs > 0 %>
|
51
95
|
<%= ChronicDuration.output(secs, :weeks => true, :units => 2) %> ago
|
52
96
|
<% else %>
|
@@ -56,15 +100,30 @@
|
|
56
100
|
<td>
|
57
101
|
<div class="progress progress-striped" style="margin-bottom: 0">
|
58
102
|
<div class='message' style='text-align:right; padding-right:0.5em; background-color: transparent; float:right;'>
|
59
|
-
<%= container
|
103
|
+
<%= container["message"] %>
|
60
104
|
</div>
|
61
|
-
<% if container
|
62
|
-
<div class="bar message" style="width: <%= container
|
63
|
-
<%= container
|
105
|
+
<% if container["pct_complete"].to_i > 0 %>
|
106
|
+
<div class="bar message" style="width: <%= container["pct_complete"] %>%;">
|
107
|
+
<%= container["pct_complete"] %>%
|
64
108
|
</div>
|
65
109
|
<% end %>
|
66
110
|
</div>
|
67
111
|
</td>
|
112
|
+
<td>
|
113
|
+
<div class="actions">
|
114
|
+
<form action="statuses" method="post">
|
115
|
+
<input type="hidden" name="jid" value="<%= container["jid"] %>" />
|
116
|
+
<%= csrf_tag %>
|
117
|
+
<% if container["status"] == "complete" %>
|
118
|
+
<input type="hidden" name="_method" value="delete" />
|
119
|
+
<input type="submit" class="btn btn-danger btn-xs" value="Remove" />
|
120
|
+
<% elsif container["status"] == "failed" %>
|
121
|
+
<input type="hidden" name="_method" value="put" />
|
122
|
+
<input type="submit" class="btn btn-warning btn-xs" value="Retry Now" />
|
123
|
+
<% end %>
|
124
|
+
</form>
|
125
|
+
</div>
|
126
|
+
</td>
|
68
127
|
</tr>
|
69
128
|
<% end %>
|
70
129
|
<% if @statuses.empty? %>
|