rocketjob_mission_control 2.1.1 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/app/assets/stylesheets/rocket_job_mission_control/callout.scss +1 -1
  4. data/app/assets/stylesheets/rocket_job_mission_control/worker_processes.scss +2 -2
  5. data/app/controllers/rocket_job_mission_control/active_workers_controller.rb +15 -0
  6. data/app/controllers/rocket_job_mission_control/dirmon_entries/index_filters_controller.rb +8 -13
  7. data/app/controllers/rocket_job_mission_control/dirmon_entries_controller.rb +3 -14
  8. data/app/controllers/rocket_job_mission_control/jobs/failures_controller.rb +6 -11
  9. data/app/controllers/rocket_job_mission_control/jobs/index_filters_controller.rb +35 -14
  10. data/app/controllers/rocket_job_mission_control/jobs_controller.rb +12 -15
  11. data/app/controllers/rocket_job_mission_control/servers/index_filters_controller.rb +49 -0
  12. data/app/controllers/rocket_job_mission_control/servers_controller.rb +93 -0
  13. data/app/datatables/rocket_job_mission_control/abstract_datatable.rb +62 -0
  14. data/app/datatables/rocket_job_mission_control/active_workers_datatable.rb +47 -0
  15. data/app/datatables/rocket_job_mission_control/completed_jobs_datatable.rb +1 -1
  16. data/app/datatables/rocket_job_mission_control/dirmon_entries_datatable.rb +10 -72
  17. data/app/datatables/rocket_job_mission_control/interrupted_jobs_datatable.rb +1 -1
  18. data/app/datatables/rocket_job_mission_control/jobs_datatable.rb +16 -91
  19. data/app/datatables/rocket_job_mission_control/queued_jobs_datatable.rb +1 -1
  20. data/app/datatables/rocket_job_mission_control/running_jobs_datatable.rb +23 -12
  21. data/app/datatables/rocket_job_mission_control/scheduled_jobs_datatable.rb +1 -1
  22. data/app/datatables/rocket_job_mission_control/servers_datatable.rb +67 -0
  23. data/app/helpers/rocket_job_mission_control/{workers_helper.rb → servers_helper.rb} +9 -9
  24. data/app/models/job_failures.rb +3 -16
  25. data/app/models/rocket_job_mission_control/query.rb +51 -0
  26. data/app/views/layouts/rocket_job_mission_control/partials/_header.html.haml +3 -3
  27. data/app/views/layouts/rocket_job_mission_control/partials/_sidebar.html.haml +2 -2
  28. data/app/views/rocket_job_mission_control/{active_processes → active_workers}/index.html.haml +3 -3
  29. data/app/views/rocket_job_mission_control/dirmon_entries/_properties.html.haml +7 -18
  30. data/app/views/rocket_job_mission_control/dirmon_entries/_status.html.haml +0 -5
  31. data/app/views/rocket_job_mission_control/dirmon_entries/show.html.haml +4 -3
  32. data/app/views/rocket_job_mission_control/jobs/_status.html.haml +22 -17
  33. data/app/views/rocket_job_mission_control/jobs/edit.html.haml +1 -1
  34. data/app/views/rocket_job_mission_control/jobs/exceptions.html.haml +29 -0
  35. data/app/views/rocket_job_mission_control/jobs/failures/index.html.haml +7 -7
  36. data/app/views/rocket_job_mission_control/jobs/show.html.haml +23 -11
  37. data/app/views/rocket_job_mission_control/servers/_actions.html.haml +9 -0
  38. data/app/views/rocket_job_mission_control/servers/_sidebar.html.haml +20 -0
  39. data/app/views/rocket_job_mission_control/{workers → servers}/index.html.haml +9 -9
  40. data/app/views/rocket_job_mission_control/{workers → servers}/index_filters/paused.html.haml +8 -8
  41. data/app/views/rocket_job_mission_control/{workers → servers}/index_filters/running.html.haml +8 -8
  42. data/app/views/rocket_job_mission_control/{workers → servers}/index_filters/starting.html.haml +8 -8
  43. data/app/views/rocket_job_mission_control/{workers → servers}/index_filters/stopping.html.haml +8 -8
  44. data/config/locales/en.yml +2 -2
  45. data/config/routes.rb +7 -6
  46. data/lib/rocket_job_mission_control/engine.rb +0 -12
  47. data/lib/rocket_job_mission_control/version.rb +1 -1
  48. data/spec/controllers/dirmon_entries/index_filters_controller_spec.rb +2 -4
  49. data/spec/controllers/dirmon_entries_controller_spec.rb +15 -72
  50. data/spec/controllers/jobs/index_filters_controller_spec.rb +1 -1
  51. data/spec/controllers/workers/index_filters_controller_spec.rb +9 -9
  52. data/spec/controllers/workers_controller_spec.rb +47 -47
  53. data/spec/dummy/config/mongoid.yml +88 -0
  54. data/spec/dummy/config/symmetric-encryption.yml +8 -0
  55. data/spec/dummy/log/test.log +89072 -0
  56. data/spec/helpers/servers_helper_spec.rb +16 -0
  57. data/spec/spec_helper.rb +3 -0
  58. metadata +31 -45
  59. data/app/controllers/rocket_job_mission_control/active_processes_controller.rb +0 -27
  60. data/app/controllers/rocket_job_mission_control/workers/index_filters_controller.rb +0 -50
  61. data/app/controllers/rocket_job_mission_control/workers_controller.rb +0 -93
  62. data/app/datatables/rocket_job_mission_control/active_processes_datatable.rb +0 -79
  63. data/app/datatables/rocket_job_mission_control/workers_datatable.rb +0 -127
  64. data/app/interactors/rocket_job_mission_control/dirmon_entries/search.rb +0 -19
  65. data/app/interactors/rocket_job_mission_control/jobs/search.rb +0 -19
  66. data/app/interactors/rocket_job_mission_control/workers/search.rb +0 -19
  67. data/app/views/rocket_job_mission_control/workers/_actions.html.haml +0 -9
  68. data/app/views/rocket_job_mission_control/workers/_sidebar.html.haml +0 -20
  69. data/spec/dummy/config/mongo.yml +0 -15
  70. data/spec/helpers/workers_helper_spec.rb +0 -16
  71. data/spec/interactors/jobs/search_spec.rb +0 -35
@@ -0,0 +1,62 @@
1
+ module RocketJobMissionControl
2
+ class AbstractDatatable
3
+ delegate :params, :link_to, :render, to: :@view
4
+ delegate :h, to: 'ERB::Util'
5
+
6
+ attr_accessor :view, :query
7
+
8
+ def initialize(view, query)
9
+ @view = view
10
+ @query = query
11
+ extract_query_params
12
+ end
13
+
14
+ def as_json(options = {})
15
+ {
16
+ draw: params[:draw].to_i,
17
+ recordsTotal: query.unfiltered_count,
18
+ recordsFiltered: query.count,
19
+ data: data(query.query)
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def data(records)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def extract_query_params
30
+ # Search term
31
+ search = params[:search]
32
+ if search.present? && search[:value].present?
33
+ query.search_term = search[:value] if search.present?
34
+ end
35
+
36
+ # Sort order
37
+ if order_by = extract_sort(params[:order])
38
+ query.order_by = order_by
39
+ end
40
+
41
+ # Pagination
42
+ unless params[:length].present? && params[:length] == '-1'
43
+ query.start = params[:start].to_i
44
+ query.page_size = params.fetch(:length, 10).to_i
45
+ end
46
+ end
47
+
48
+ def extract_sort(order)
49
+ return nil unless order.present?
50
+
51
+ ap order
52
+ sort_by = {}
53
+ order.each_pair do |key, value|
54
+ name = query.display_columns[value[:column].to_i]
55
+ raise(ArgumentError, "Invalid column id: #{value[:column]}. Must fit #{query.display_columns.inspect}") unless name.present?
56
+ sort_by[name] = value[:dir]
57
+ end
58
+ sort_by
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ module RocketJobMissionControl
2
+ class ActiveWorkersDatatable < AbstractDatatable
3
+ delegate :job_path, :state_icon, to: :@view
4
+
5
+ def initialize(view, query)
6
+ super(view, query)
7
+ end
8
+
9
+ private
10
+
11
+ def extract_query_params
12
+ @query.order_by = nil
13
+ end
14
+
15
+ def data(active_workers)
16
+ active_workers.collect do |active_worker|
17
+ {
18
+ '0' => worker_name_with_icon(active_worker, active_worker.job),
19
+ '1' => job_name_with_link(active_worker.job.class.name, active_worker.job.id),
20
+ '2' => h(active_worker.job.description.try!(:truncate, 50)),
21
+ '3' => h("#{active_worker.duration} ago"),
22
+ 'DT_RowClass' => 'card callout callout-running'
23
+ }
24
+ end
25
+ end
26
+
27
+ def worker_name_with_icon(active_worker, job)
28
+ state = active_worker.zombie? ? :zombie : job.state
29
+ <<-EOS
30
+ <i class="fa #{state_icon(state)}" style="font-size: 75%" title="#{state}"></i>
31
+ #{active_worker.name}
32
+ EOS
33
+ end
34
+
35
+ def job_name_with_link(job_class_name, job_id)
36
+ <<-EOS
37
+ <a href="#{job_path(job_id)}">
38
+ #{job_class_name}
39
+ </a>
40
+ EOS
41
+ end
42
+
43
+ def duration(started_at)
44
+ "#{RocketJob.seconds_as_duration(Time.now - started_at)} ago" if started_at
45
+ end
46
+ end
47
+ end
@@ -2,7 +2,7 @@ module RocketJobMissionControl
2
2
  class CompletedJobsDatatable < JobsDatatable
3
3
  private
4
4
 
5
- def data
5
+ def data(jobs)
6
6
  jobs.map do |job|
7
7
  {
8
8
  '0' => class_with_link(job),
@@ -1,88 +1,26 @@
1
1
  module RocketJobMissionControl
2
- class DirmonEntriesDatatable
3
- delegate :params,
4
- :link_to,
5
- :dirmon_entry_path,
6
- :state_icon,
7
- :render, to: :@view
2
+ class DirmonEntriesDatatable < AbstractDatatable
3
+ delegate :dirmon_entry_path, :state_icon, to: :@view
8
4
 
9
- delegate :h, to: 'ERB::Util'
10
-
11
- def initialize(view, dirmons)
12
- @view = view
13
- @unfiltered_dirmons = dirmons
14
- end
15
-
16
- def as_json(options = {})
17
- {
18
- :draw => params[:draw].to_i,
19
- :recordsTotal => get_raw_records.count,
20
- :recordsFiltered => filter_records(get_raw_records).count,
21
- :data => data
22
- }
5
+ def initialize(view, query)
6
+ query.display_columns = %w[name _type pattern]
7
+ query.search_columns = [:job_class_name, :name, :pattern]
8
+ super(view, query)
23
9
  end
24
10
 
25
11
  private
26
12
 
27
- def data
13
+ def data(dirmons)
28
14
  dirmons.map do |dirmon|
29
15
  {
30
- '0' => name_with_link(dirmon),
31
- '1' => h(dirmon.job_class_name),
32
- '2' => h(dirmon.pattern.try(:truncate, 80)),
16
+ '0' => name_with_link(dirmon),
17
+ '1' => h(dirmon.job_class_name),
18
+ '2' => h(dirmon.pattern.try(:truncate, 80)),
33
19
  'DT_RowClass' => "card callout callout-#{dirmon.state}"
34
20
  }
35
21
  end
36
22
  end
37
23
 
38
- def get_raw_records
39
- @unfiltered_dirmons
40
- end
41
-
42
- def dirmons
43
- @dirmons ||= fetch_dirmons
44
- end
45
-
46
- def fetch_dirmons
47
- records = get_raw_records
48
- records = sort_records(records) if params[:order].present?
49
- records = filter_records(records) if params[:search].present?
50
- records = paginate_records(records) unless params[:length].present? && params[:length] == '-1'
51
- records
52
- end
53
-
54
- def page
55
- (params[:start].to_i / per_page) + 1
56
- end
57
-
58
- def per_page
59
- params.fetch(:length, 10).to_i
60
- end
61
-
62
- def sort_records(records)
63
- sort_by = {}
64
- params[:order].keys.each do |key|
65
- sort_by[sort_column(params[:order][key][:column])] = params[:order][key][:dir]
66
- end
67
- records.sort(sort_by)
68
- end
69
-
70
- def sort_column(index)
71
- columns = %w[name max_threads started_at heartbeat.updated_at]
72
- columns[index.to_i]
73
- end
74
-
75
- def filter_records(records)
76
- return records unless (params[:search].present? && params[:search][:value].present?)
77
- conditions = params[:search][:value]#build_conditions_for(params[:search][:value])
78
- records = RocketJobMissionControl::DirmonEntries::Search.new(conditions, records).execute if conditions
79
- records
80
- end
81
-
82
- def paginate_records(records)
83
- Kaminari.paginate_array(records.all).page(page).per(per_page)
84
- end
85
-
86
24
  def name_with_link(dirmon)
87
25
  <<-EOS
88
26
  <a href="#{dirmon_entry_path(dirmon.id)}">
@@ -2,7 +2,7 @@ module RocketJobMissionControl
2
2
  class InterruptedJobsDatatable < JobsDatatable
3
3
  private
4
4
 
5
- def data
5
+ def data(jobs)
6
6
  jobs.map do |job|
7
7
  {
8
8
  '0' => class_with_link(job),
@@ -1,55 +1,29 @@
1
1
  module RocketJobMissionControl
2
- class JobsDatatable
3
- delegate :params, :link_to, :job_path, :job_icon, :edit_job_path,
4
- :abort_job_path, :job_path, :fail_job_path, :run_now_job_path, :pause_job_path,
5
- :resume_job_path, :retry_job_path, :job_failures_path, :job_action_link, to: :@view
6
- delegate :h, to: 'ERB::Util'
2
+ class JobsDatatable < AbstractDatatable
3
+ delegate :job_path, :job_icon, :edit_job_path,
4
+ :abort_job_path, :job_path, :fail_job_path, :run_now_job_path, :pause_job_path,
5
+ :resume_job_path, :retry_job_path, :job_failures_path, :job_action_link, :exceptions_job_path, to: :@view
7
6
 
8
- def initialize(view, jobs)
9
- @view = view
10
- @unfiltered_jobs = jobs
11
- end
12
-
13
- def as_json(options = {})
14
- {
15
- :draw => params[:draw].to_i,
16
- :recordsTotal => get_raw_records.count,
17
- :recordsFiltered => filter_records(get_raw_records).count,
18
- :data => data
19
- }
7
+ def initialize(view, query)
8
+ query.search_columns = [:_type, :description]
9
+ super(view, query)
20
10
  end
21
11
 
22
12
  private
23
13
 
24
- def data
14
+ def data(jobs)
25
15
  jobs.map do |job|
26
16
  {
27
- '0' => class_with_link(job),
28
- '1' => h(job.description.try(:truncate, 50)),
29
- '2' => h(job.created_at),
30
- '3' => h(job.duration),
31
- '4' => action_buttons(job),
17
+ '0' => class_with_link(job),
18
+ '1' => h(job.description.try(:truncate, 50)),
19
+ '2' => h(job.created_at),
20
+ '3' => h(job.duration),
21
+ '4' => action_buttons(job),
32
22
  'DT_RowClass' => "card callout callout-#{job.state}"
33
23
  }
34
24
  end
35
25
  end
36
26
 
37
- def get_raw_records
38
- @unfiltered_jobs
39
- end
40
-
41
- def jobs
42
- @jobs ||= fetch_jobs
43
- end
44
-
45
- def fetch_jobs
46
- records = get_raw_records
47
- records = sort_records(records) if params[:order].present?
48
- records = filter_records(records) if params[:search].present?
49
- records = paginate_records(records) unless params[:length].present? && params[:length] == '-1'
50
- records
51
- end
52
-
53
27
  def class_with_link(job)
54
28
  <<-EOS
55
29
  <a class='job-link' href="#{job_path(job.id)}">
@@ -60,23 +34,11 @@ module RocketJobMissionControl
60
34
  end
61
35
 
62
36
  def action_buttons(job)
63
- events = valid_events(job)
37
+ events = valid_events(job)
64
38
  buttons = "<div class='inline-job-actions'>"
65
- unless job.completed? || job.aborted?
66
- buttons += "#{ link_to( 'Edit', edit_job_path(job), class: 'btn btn-default', title: 'Edit job') }"
67
- end
68
39
  if job.scheduled?
69
40
  buttons += "#{ job_action_link('Run', run_now_job_path(job), :patch) }"
70
41
  end
71
- if events.include?(:abort)
72
- buttons += "#{ job_action_link('Abort', abort_job_path(job), :patch) }"
73
- end
74
- if job.completed? || job.aborted?
75
- buttons += "#{ job_action_link('Destroy', job_path(job), :delete) }"
76
- end
77
- if events.include?(:fail)
78
- buttons += "#{ job_action_link('Fail', fail_job_path(job), :patch) }"
79
- end
80
42
  if events.include?(:pause)
81
43
  buttons += "#{ job_action_link('Pause', pause_job_path(job), :patch) }"
82
44
  end
@@ -86,50 +48,13 @@ module RocketJobMissionControl
86
48
  if events.include?(:retry)
87
49
  buttons += "#{ job_action_link('Retry', retry_job_path(job), :patch) }"
88
50
  end
89
- if job.respond_to?(:input) && job.input.failed_count > 0
90
- buttons += "#{ link_to('View Errors', job_failures_path(job), class: 'btn btn-default') }"
91
- end
51
+ buttons += "#{ job_action_link('Destroy', job_path(job), :delete) }"
92
52
  buttons += "</div>"
93
53
  end
94
54
 
95
55
  def valid_events(job)
96
- job.aasm.events.collect{ |e| e.name }
56
+ job.aasm.events.collect(&:name)
97
57
  end
98
58
 
99
- def page
100
- (params[:start].to_i / per_page) + 1
101
- end
102
-
103
- def per_page
104
- params.fetch(:length, 10).to_i
105
- end
106
-
107
- def sort_records(records)
108
- sort_by = {}
109
- params[:order].keys.each do |key|
110
- sort_by[sort_column(params[:order][key][:column])] = params[:order][key][:dir]
111
- end
112
- records.sort(sort_by)
113
- end
114
-
115
- def counts
116
- RocketJob::Job.counts_by_state
117
- end
118
-
119
- def sort_column(index)
120
- columns = %w[_type description completed_at]
121
- columns[index.to_i]
122
- end
123
-
124
- def filter_records(records)
125
- return records unless (params[:search].present? && params[:search][:value].present?)
126
- conditions = params[:search][:value]
127
- records = RocketJobMissionControl::Jobs::Search.new(conditions, records).execute if conditions
128
- records
129
- end
130
-
131
- def paginate_records(records)
132
- Kaminari.paginate_array(records.all).page(page).per(per_page)
133
- end
134
59
  end
135
60
  end
@@ -2,7 +2,7 @@ module RocketJobMissionControl
2
2
  class QueuedJobsDatatable < JobsDatatable
3
3
  private
4
4
 
5
- def data
5
+ def data(jobs)
6
6
  jobs.map do |job|
7
7
  {
8
8
  '0' => class_with_link(job),
@@ -2,15 +2,15 @@ module RocketJobMissionControl
2
2
  class RunningJobsDatatable < JobsDatatable
3
3
  private
4
4
 
5
- def data
5
+ def data(jobs)
6
6
  jobs.map do |job|
7
7
  {
8
- '0' => class_with_link(job),
9
- '1' => h(job.description.try(:truncate, 50)),
10
- '2' => progress(job),
11
- '3' => h(job.priority),
12
- '4' => h(started(job)),
13
- '5' => action_buttons(job),
8
+ '0' => class_with_link(job),
9
+ '1' => h(job.description.try(:truncate, 50)),
10
+ '2' => progress(job),
11
+ '3' => h(job.priority),
12
+ '4' => h(started(job)),
13
+ '5' => action_buttons(job),
14
14
  'DT_RowClass' => "card callout callout-#{job.state}"
15
15
  }
16
16
  end
@@ -22,11 +22,22 @@ module RocketJobMissionControl
22
22
  end
23
23
 
24
24
  def progress(job)
25
- <<-EOS
26
- <div class='progress'>
27
- <div class='progress-bar' style="width: #{job.percent_complete}%;", title="#{job.percent_complete}% complete."></div>
28
- </div>
29
- EOS
25
+ if (sub_state = job.attributes['sub_state']) && [:before, :after].include?(sub_state)
26
+ <<-EOS
27
+ <div class="job-status">
28
+ <div class="job-state">
29
+ <div class="left">Batch</div>
30
+ <div class="right running">#{sub_state}</div>
31
+ </div>
32
+ </div>
33
+ EOS
34
+ else
35
+ <<-EOS
36
+ <div class='progress'>
37
+ <div class='progress-bar' style="width: #{job.percent_complete}%;", title="#{job.percent_complete}% complete."></div>
38
+ </div>
39
+ EOS
40
+ end
30
41
  end
31
42
 
32
43
  def started(job)
@@ -2,7 +2,7 @@ module RocketJobMissionControl
2
2
  class ScheduledJobsDatatable < JobsDatatable
3
3
  private
4
4
 
5
- def data
5
+ def data(jobs)
6
6
  jobs.map do |job|
7
7
  {
8
8
  '0' => class_with_link(job),
@@ -0,0 +1,67 @@
1
+ module RocketJobMissionControl
2
+ class ServersDatatable < AbstractDatatable
3
+ delegate :server_icon, :server_path, :stop_server_path, :resume_server_path, :pause_server_path, :server_card_class, to: :@view
4
+
5
+ def initialize(view, query)
6
+ query.display_columns = %w[name heartbeat.workers started_at heartbeat.updated_at]
7
+ query.search_columns = [:name]
8
+ super(view, query)
9
+ end
10
+
11
+ private
12
+
13
+ def data(servers)
14
+ servers.collect do |server|
15
+ {
16
+ '0' => name_with_icon(server),
17
+ '1' => h(threads(server)),
18
+ '2' => h(started_ago(server)),
19
+ '3' => h(time_since_heartbeat(server)),
20
+ '4' => action_links_html(server),
21
+ 'DT_RowClass' => "card callout #{server_card_class(server)}"
22
+ }
23
+ end
24
+ end
25
+
26
+ def name_with_icon(server)
27
+ <<-EOS
28
+ <i class="fa #{server_icon(server)}" style="font-size: 75%" title="#{server.state}"></i>
29
+ #{server.name}
30
+ EOS
31
+ end
32
+
33
+ def threads(server)
34
+ "#{server.heartbeat.workers.to_i}/#{server.max_workers}"
35
+ end
36
+
37
+ def started_ago(server)
38
+ "#{RocketJob.seconds_as_duration(Time.now - server.started_at)} ago"
39
+ end
40
+
41
+ def time_since_heartbeat(server)
42
+ "#{RocketJob.seconds_as_duration(Time.now - server.heartbeat.updated_at)} ago"
43
+ end
44
+
45
+ def action_links_html(server)
46
+ actions = '<div class="actions">'
47
+ if server.stopping?
48
+ actions += "Server is stopping..."
49
+ confirmation = ''
50
+ unless server.zombie?
51
+ confirmation << "Warning!\n\nDestroying this server will hard kill its active workers/jobs.\nKilled jobs will be requeued for processing on another worker.\n\n"
52
+ end
53
+ confirmation << "Are you sure you want to destroy #{server.name} ?"
54
+ actions += "#{ link_to "destroy", server_path(server), method: :delete, class: 'btn btn-danger', data: {confirm: confirmation} }"
55
+ else
56
+ if server.paused?
57
+ actions += "#{ link_to "resume", resume_server_path(server), method: :patch, class: 'btn btn-default', data: {confirm: "Resume this server?"} }"
58
+ else
59
+ actions += "#{ link_to "pause", pause_server_path(server), method: :patch, class: 'btn btn-default', data: {confirm: "Pause this server?"} }"
60
+ end
61
+ actions += "#{ link_to "stop", stop_server_path(server), method: :patch, class: 'btn btn-danger', data: {confirm: "Stop this server?"} }"
62
+ end
63
+ actions += '</div>'
64
+ end
65
+
66
+ end
67
+ end