sidekiq-status 0.7.0 → 0.8.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.
- 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? %>
|