rocketjob_mission_control 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +26 -0
- data/app/assets/javascripts/rocket_job_mission_control/application.js +18 -0
- data/app/assets/javascripts/rocket_job_mission_control/base.js.coffee +24 -0
- data/app/assets/javascripts/rocket_job_mission_control/jobs.js.coffee +11 -0
- data/app/assets/stylesheets/rocket_job_mission_control/application.scss +18 -0
- data/app/assets/stylesheets/rocket_job_mission_control/base.scss +468 -0
- data/app/assets/stylesheets/rocket_job_mission_control/bootstrap_and_overrides.scss +6 -0
- data/app/assets/stylesheets/rocket_job_mission_control/callout.scss +40 -0
- data/app/assets/stylesheets/rocket_job_mission_control/jobs.scss +16 -0
- data/app/assets/stylesheets/rocket_job_mission_control/jquery.bootstrap-touchspin.scss +45 -0
- data/app/controllers/rocket_job_mission_control/application_controller.rb +15 -0
- data/app/controllers/rocket_job_mission_control/jobs/inputs_controller.rb +9 -0
- data/app/controllers/rocket_job_mission_control/jobs/slices_controller.rb +10 -0
- data/app/controllers/rocket_job_mission_control/jobs_controller.rb +97 -0
- data/app/controllers/rocket_job_mission_control/workers_controller.rb +79 -0
- data/app/helpers/rocket_job_mission_control/application_helper.rb +16 -0
- data/app/helpers/rocket_job_mission_control/jobs_helper.rb +57 -0
- data/app/helpers/rocket_job_mission_control/workers_helper.rb +14 -0
- data/app/views/layouts/rocket_job_mission_control/application.html.haml +43 -0
- data/app/views/layouts/rocket_job_mission_control/partials/_header.html.haml +16 -0
- data/app/views/layouts/rocket_job_mission_control/partials/_sidebar.html.haml +20 -0
- data/app/views/rocket_job_mission_control/jobs/_list.html.haml +33 -0
- data/app/views/rocket_job_mission_control/jobs/_status.html.haml +39 -0
- data/app/views/rocket_job_mission_control/jobs/index.html.haml +8 -0
- data/app/views/rocket_job_mission_control/jobs/running.html.haml +37 -0
- data/app/views/rocket_job_mission_control/jobs/show.html.haml +50 -0
- data/app/views/rocket_job_mission_control/workers/_actions.html.haml +9 -0
- data/app/views/rocket_job_mission_control/workers/index.html.haml +51 -0
- data/config/locales/en.yml +57 -0
- data/config/routes.rb +28 -0
- data/lib/rocket_job_mission_control/engine.rb +15 -0
- data/lib/rocket_job_mission_control/version.rb +3 -0
- data/lib/rocketjob_mission_control.rb +4 -0
- data/lib/tasks/rocket_job_mission_control_tasks.rake +4 -0
- data/spec/controllers/application_controller_spec.rb +47 -0
- data/spec/controllers/jobs_controller_spec.rb +217 -0
- data/spec/controllers/workers_controller_spec.rb +125 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +25 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/log/test.log +2459 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/helpers/jobs_helper_spec.rb +100 -0
- data/spec/rails_helper.rb +47 -0
- data/spec/spec_helper.rb +81 -0
- data/spec/views/workers/index.html.haml_spec.rb +17 -0
- data/vendor/assets/javascripts/jquery.bootstrap-touchspin.js +686 -0
- data/vendor/assets/javascripts/prism.js +6 -0
- data/vendor/assets/stylesheets/jquery.bootstrap-touchspin.css +45 -0
- data/vendor/assets/stylesheets/prism.scss +160 -0
- metadata +319 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
.callout {
|
2
|
+
border: 1px solid #eee;
|
3
|
+
border-left-width: 0.5em;
|
4
|
+
}
|
5
|
+
|
6
|
+
.callout-top {
|
7
|
+
border: 1px solid #eee;
|
8
|
+
border-top-width: 5px;
|
9
|
+
}
|
10
|
+
|
11
|
+
.callout-info-top {
|
12
|
+
border-top-color: #1b809e;
|
13
|
+
}
|
14
|
+
|
15
|
+
.callout-success-top {
|
16
|
+
border-top-color: #3c763d;
|
17
|
+
}
|
18
|
+
|
19
|
+
.callout-warning-top {
|
20
|
+
border-top-color: #aa6708;
|
21
|
+
}
|
22
|
+
|
23
|
+
.callout-alert-top {
|
24
|
+
border-top-color: #ce4844;
|
25
|
+
}
|
26
|
+
.callout-info {
|
27
|
+
border-left-color: #1b809e;
|
28
|
+
}
|
29
|
+
|
30
|
+
.callout-success {
|
31
|
+
border-left-color: #3c763d;
|
32
|
+
}
|
33
|
+
|
34
|
+
.callout-warning {
|
35
|
+
border-left-color: #aa6708;
|
36
|
+
}
|
37
|
+
|
38
|
+
.callout-alert {
|
39
|
+
border-left-color: #ce4844;
|
40
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
.job-list {
|
2
|
+
.list {
|
3
|
+
.progress {
|
4
|
+
width: 16em;
|
5
|
+
height: 1em;
|
6
|
+
margin: 5px 0px 0px 10px;
|
7
|
+
-webkit-border-radius: 0 !important;
|
8
|
+
-moz-border-radius: 0 !important;
|
9
|
+
border-radius: 0 !important;
|
10
|
+
}
|
11
|
+
|
12
|
+
.progress-bar {
|
13
|
+
min-width: 1em;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
/*
|
2
|
+
* Bootstrap TouchSpin - v3.0.1
|
3
|
+
* A mobile and touch friendly input spinner component for Bootstrap 3.
|
4
|
+
* http://www.virtuosoft.eu/code/bootstrap-touchspin/
|
5
|
+
*
|
6
|
+
* Made by István Ujj-Mészáros
|
7
|
+
* Under Apache License v2.0 License
|
8
|
+
*/
|
9
|
+
|
10
|
+
.bootstrap-touchspin .input-group-btn-vertical {
|
11
|
+
position: relative;
|
12
|
+
white-space: nowrap;
|
13
|
+
width: 1%;
|
14
|
+
vertical-align: middle;
|
15
|
+
display: table-cell;
|
16
|
+
}
|
17
|
+
|
18
|
+
.bootstrap-touchspin .input-group-btn-vertical > .btn {
|
19
|
+
display: block;
|
20
|
+
float: none;
|
21
|
+
width: 100%;
|
22
|
+
max-width: 100%;
|
23
|
+
padding: 8px 10px;
|
24
|
+
margin-left: -1px;
|
25
|
+
position: relative;
|
26
|
+
}
|
27
|
+
|
28
|
+
.bootstrap-touchspin .input-group-btn-vertical .bootstrap-touchspin-up {
|
29
|
+
border-radius: 0;
|
30
|
+
border-top-right-radius: 4px;
|
31
|
+
}
|
32
|
+
|
33
|
+
.bootstrap-touchspin .input-group-btn-vertical .bootstrap-touchspin-down {
|
34
|
+
margin-top: -2px;
|
35
|
+
border-radius: 0;
|
36
|
+
border-bottom-right-radius: 4px;
|
37
|
+
}
|
38
|
+
|
39
|
+
.bootstrap-touchspin .input-group-btn-vertical i {
|
40
|
+
position: absolute;
|
41
|
+
top: 3px;
|
42
|
+
left: 5px;
|
43
|
+
font-size: 9px;
|
44
|
+
font-weight: normal;
|
45
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
include ActionController::Live
|
4
|
+
|
5
|
+
around_action :with_time_zone
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def with_time_zone
|
10
|
+
if time_zone = session['time_zone'] || 'UTC'
|
11
|
+
Time.use_zone(time_zone) { yield }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
class JobsController < RocketJobMissionControl::ApplicationController
|
3
|
+
before_filter :find_job_or_redirect, except: [:index, :running]
|
4
|
+
rescue_from StandardError, with: :error_occurred
|
5
|
+
|
6
|
+
def running
|
7
|
+
@jobs = RocketJob::Job.where(state: 'running')
|
8
|
+
end
|
9
|
+
|
10
|
+
def update
|
11
|
+
@job.update_attributes!(job_params)
|
12
|
+
|
13
|
+
redirect_to job_path(@job)
|
14
|
+
end
|
15
|
+
|
16
|
+
def abort
|
17
|
+
@job.abort!
|
18
|
+
|
19
|
+
redirect_to(job_path(@job))
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy
|
23
|
+
if @job.completed? || @job.aborted?
|
24
|
+
@job.destroy
|
25
|
+
redirect_to(jobs_path)
|
26
|
+
else
|
27
|
+
flash[:alert] = "Cannot destroy a job unless it is completed or aborted"
|
28
|
+
redirect_to(job_path(@job))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def retry
|
33
|
+
@job.retry!
|
34
|
+
|
35
|
+
redirect_to(job_path(@job))
|
36
|
+
end
|
37
|
+
|
38
|
+
def pause
|
39
|
+
@job.pause!
|
40
|
+
|
41
|
+
redirect_to(job_path(@job))
|
42
|
+
end
|
43
|
+
|
44
|
+
def resume
|
45
|
+
@job.resume!
|
46
|
+
|
47
|
+
redirect_to(job_path(@job))
|
48
|
+
end
|
49
|
+
|
50
|
+
def fail
|
51
|
+
@job.fail!
|
52
|
+
|
53
|
+
redirect_to(job_path(@job))
|
54
|
+
end
|
55
|
+
|
56
|
+
def show
|
57
|
+
load_jobs
|
58
|
+
end
|
59
|
+
|
60
|
+
def index
|
61
|
+
load_jobs
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def load_jobs
|
67
|
+
@states = jobs_params
|
68
|
+
@jobs = RocketJob::Job.limit(1000).sort(created_at: :desc)
|
69
|
+
@jobs = @jobs.where(state: @states) unless @states.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_job_or_redirect
|
73
|
+
@job = RocketJob::Job.find(params[:id])
|
74
|
+
|
75
|
+
if @job.nil?
|
76
|
+
flash[:alert] = t(:failure, scope: [:job, :find], id: params[:id])
|
77
|
+
|
78
|
+
redirect_to(jobs_path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def jobs_params
|
83
|
+
params.fetch(:states, [])
|
84
|
+
end
|
85
|
+
|
86
|
+
def job_params
|
87
|
+
params.require(:job).permit(:priority)
|
88
|
+
end
|
89
|
+
|
90
|
+
def error_occurred(exception)
|
91
|
+
logger.error "Error loading a job", exception
|
92
|
+
flash[:danger] = "Error loading jobs."
|
93
|
+
raise exception if Rails.env.development?
|
94
|
+
redirect_to :back
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
class WorkersController < RocketJobMissionControl::ApplicationController
|
3
|
+
before_filter :find_worker, only: [:stop, :pause, :resume, :destroy]
|
4
|
+
|
5
|
+
def index
|
6
|
+
@workers = RocketJob::Worker.sort(:name)
|
7
|
+
end
|
8
|
+
|
9
|
+
VALID_STATES = { stop: 'stopped', pause: 'paused', resume: 'resumed' }
|
10
|
+
|
11
|
+
def update_all
|
12
|
+
worker_action = params[:worker_action].to_sym
|
13
|
+
if VALID_STATES.keys.include?(worker_action)
|
14
|
+
RocketJob::Worker.send("#{worker_action}_all".to_sym)
|
15
|
+
flash[:notice] = t(:success, scope: [:worker, :update_all], worker_action: VALID_STATES[worker_action])
|
16
|
+
else
|
17
|
+
flash[:alert] = t(:invalid, scope: [:worker, :update_all])
|
18
|
+
end
|
19
|
+
|
20
|
+
respond_to do |format|
|
21
|
+
format.html { redirect_to workers_path }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop
|
26
|
+
if @worker.stop!
|
27
|
+
flash[:notice] = t(:success, scope: [:worker, :stop])
|
28
|
+
else
|
29
|
+
flash[:alert] = t(:failure, scope: [:worker, :stop])
|
30
|
+
end
|
31
|
+
|
32
|
+
respond_to do |format|
|
33
|
+
format.html { redirect_to workers_path }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy
|
38
|
+
if @worker.destroy
|
39
|
+
flash[:notice] = t(:success, scope: [:worker, :destroy])
|
40
|
+
else
|
41
|
+
flash[:alert] = t(:failure, scope: [:worker, :destroy])
|
42
|
+
end
|
43
|
+
|
44
|
+
respond_to do |format|
|
45
|
+
format.html { redirect_to workers_path }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def pause
|
50
|
+
if @worker.pause!
|
51
|
+
flash[:notice] = t(:success, scope: [:worker, :pause])
|
52
|
+
else
|
53
|
+
flash[:alert] = t(:failure, scope: [:worker, :pause])
|
54
|
+
end
|
55
|
+
|
56
|
+
respond_to do |format|
|
57
|
+
format.html { redirect_to workers_path }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def resume
|
62
|
+
if @worker.resume!
|
63
|
+
flash[:notice] = t(:success, scope: [:worker, :resume])
|
64
|
+
else
|
65
|
+
flash[:alert] = t(:failure, scope: [:worker, :resume])
|
66
|
+
end
|
67
|
+
|
68
|
+
respond_to do |format|
|
69
|
+
format.html { redirect_to workers_path }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def find_worker
|
76
|
+
@worker = RocketJob::Worker.find(params[:id])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
module ApplicationHelper
|
3
|
+
def site_title
|
4
|
+
'Rocket Job Mission Control'
|
5
|
+
end
|
6
|
+
|
7
|
+
def title
|
8
|
+
@page_title ||= params[:controller].to_s.titleize
|
9
|
+
h(@full_title || [@page_title, site_title].compact.join(' | '))
|
10
|
+
end
|
11
|
+
|
12
|
+
def active_page(path)
|
13
|
+
"active" if current_page?(path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
module JobsHelper
|
3
|
+
STATE_ICON_MAP = {
|
4
|
+
queued: 'fa-bed warning',
|
5
|
+
paused: 'fa-pause warning',
|
6
|
+
running: 'fa-cog fa-spin primary',
|
7
|
+
completed: 'fa-check success',
|
8
|
+
aborted: 'fa-times warning',
|
9
|
+
}
|
10
|
+
|
11
|
+
STATE_CLASS_MAP = {
|
12
|
+
queued: 'warning',
|
13
|
+
paused: 'warning',
|
14
|
+
running: 'primary',
|
15
|
+
completed: 'success',
|
16
|
+
aborted: 'danger',
|
17
|
+
failed: 'danger',
|
18
|
+
}
|
19
|
+
|
20
|
+
def job_state_icon(state)
|
21
|
+
STATE_ICON_MAP[state.to_sym] || 'fa-times danger'
|
22
|
+
end
|
23
|
+
|
24
|
+
def job_class(job)
|
25
|
+
STATE_CLASS_MAP[job.state.to_sym] || ""
|
26
|
+
end
|
27
|
+
|
28
|
+
def pretty_print_array_or_hash(arguments)
|
29
|
+
return arguments unless arguments.kind_of?(Array) || arguments.kind_of?(Hash)
|
30
|
+
json_string_options = {space: ' ', indent: ' ', array_nl: '<br />', object_nl: '<br />'}
|
31
|
+
JSON.generate(arguments, json_string_options).html_safe
|
32
|
+
end
|
33
|
+
|
34
|
+
def job_card_class(job)
|
35
|
+
map = {
|
36
|
+
running: 'callout-info',
|
37
|
+
completed: 'callout-success',
|
38
|
+
failed: 'callout-alert',
|
39
|
+
aborted: 'callout-warning',
|
40
|
+
}
|
41
|
+
"card callout " << map[job.state.to_sym].to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def job_selected_class(job, selected_job = nil)
|
45
|
+
if selected_job.present? && job.id == selected_job.id
|
46
|
+
' text-info'
|
47
|
+
else
|
48
|
+
' text-muted'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def job_title(job)
|
53
|
+
perform_method = job.perform_method == :perform ? '' : "##{job.perform_method}"
|
54
|
+
"#{job.class.name}#{perform_method}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
module WorkersHelper
|
3
|
+
|
4
|
+
def worker_card_class(worker)
|
5
|
+
map = {
|
6
|
+
running: 'callout-success-top',
|
7
|
+
paused: 'callout-warning-top',
|
8
|
+
stopping: 'callout-alert-top',
|
9
|
+
}
|
10
|
+
map[worker.state] || 'callout-info-top'
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title= title
|
5
|
+
|
6
|
+
%link{rel: "icon", type: "image/png", href: "/assets/favicon.png"}
|
7
|
+
%link{rel: "apple-touch-icon-precomposed", href: "/assets/apple-touch-icon.png"}
|
8
|
+
|
9
|
+
%meta{'http-equiv' => "Content-Type", content: "text/html; charset=UTF-8"}
|
10
|
+
%meta{'http-equiv' => "Content-Script-Type", content: "text/javascript"}
|
11
|
+
%meta{'http-equiv' =>"X-UA-Compatible", content: "IE=edge" }
|
12
|
+
%meta{name: "viewport", content: "width=device-width, initial-scale=1"}
|
13
|
+
= yield :meta
|
14
|
+
|
15
|
+
|
16
|
+
= stylesheet_link_tag "rocket_job_mission_control/application", :media => "all"
|
17
|
+
= javascript_include_tag "rocket_job_mission_control/application"
|
18
|
+
|
19
|
+
= csrf_meta_tag
|
20
|
+
|
21
|
+
%link{href: "//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css", rel: "stylesheet"}/
|
22
|
+
|
23
|
+
%body.rocket_job
|
24
|
+
#wrapper.toggled
|
25
|
+
= render partial: 'layouts/rocket_job_mission_control/partials/sidebar'
|
26
|
+
|
27
|
+
#page-content-wrapper
|
28
|
+
= render partial: 'layouts/rocket_job_mission_control/partials/header'
|
29
|
+
|
30
|
+
.flash
|
31
|
+
- flash.each do |key, msg|
|
32
|
+
- if msg
|
33
|
+
.alert{class: "alert-#{key} alert-dismissable", role: 'alert'}
|
34
|
+
%button.close{ data: { dismiss: 'alert' } }
|
35
|
+
%span ×
|
36
|
+
-if msg.kind_of? Array
|
37
|
+
%ul.list-unstyled.margin-bottom-0
|
38
|
+
- msg.each do |m|
|
39
|
+
%li= m.html_safe
|
40
|
+
- else
|
41
|
+
= msg.html_safe
|
42
|
+
|
43
|
+
= yield
|