solid_job_monitor 1.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +106 -0
- data/Rakefile +8 -0
- data/app/assets/config/mission_control_jobs_manifest.js +4 -0
- data/app/assets/stylesheets/mission_control/jobs/application.css +16 -0
- data/app/assets/stylesheets/mission_control/jobs/bulma.min.css +21013 -0
- data/app/assets/stylesheets/mission_control/jobs/forms.css +8 -0
- data/app/assets/stylesheets/mission_control/jobs/jobs.css +68 -0
- data/app/controllers/concerns/mission_control/jobs/adapter_features.rb +24 -0
- data/app/controllers/concerns/mission_control/jobs/application_scoped.rb +38 -0
- data/app/controllers/concerns/mission_control/jobs/basic_authentication.rb +33 -0
- data/app/controllers/concerns/mission_control/jobs/failed_jobs_bulk_operations.rb +17 -0
- data/app/controllers/concerns/mission_control/jobs/job_filters.rb +41 -0
- data/app/controllers/concerns/mission_control/jobs/job_scoped.rb +16 -0
- data/app/controllers/concerns/mission_control/jobs/not_found_redirections.rb +36 -0
- data/app/controllers/concerns/mission_control/jobs/queue_scoped.rb +12 -0
- data/app/controllers/mission_control/jobs/application_controller.rb +32 -0
- data/app/controllers/mission_control/jobs/bulk_discards_controller.rb +20 -0
- data/app/controllers/mission_control/jobs/bulk_retries_controller.rb +10 -0
- data/app/controllers/mission_control/jobs/discards_controller.rb +18 -0
- data/app/controllers/mission_control/jobs/dispatches_controller.rb +18 -0
- data/app/controllers/mission_control/jobs/jobs_controller.rb +54 -0
- data/app/controllers/mission_control/jobs/progresses_controller.rb +27 -0
- data/app/controllers/mission_control/jobs/queues/pauses_controller.rb +15 -0
- data/app/controllers/mission_control/jobs/queues_controller.rb +16 -0
- data/app/controllers/mission_control/jobs/recurring_tasks_controller.rb +38 -0
- data/app/controllers/mission_control/jobs/retries_controller.rb +13 -0
- data/app/controllers/mission_control/jobs/stops_controller.rb +22 -0
- data/app/controllers/mission_control/jobs/workers_controller.rb +23 -0
- data/app/helpers/mission_control/jobs/application_helper.rb +9 -0
- data/app/helpers/mission_control/jobs/dates_helper.rb +19 -0
- data/app/helpers/mission_control/jobs/interface_helper.rb +23 -0
- data/app/helpers/mission_control/jobs/jobs_helper.rb +94 -0
- data/app/helpers/mission_control/jobs/navigation_helper.rb +60 -0
- data/app/javascript/mission_control/jobs/application.js +4 -0
- data/app/javascript/mission_control/jobs/controllers/application.js +9 -0
- data/app/javascript/mission_control/jobs/controllers/form_controller.js +21 -0
- data/app/javascript/mission_control/jobs/controllers/index.js +11 -0
- data/app/javascript/mission_control/jobs/controllers/progress_controller.js +55 -0
- data/app/javascript/mission_control/jobs/helpers/debounce_helpers.js +9 -0
- data/app/javascript/mission_control/jobs/helpers/index.js +1 -0
- data/app/models/mission_control/jobs/application_record.rb +7 -0
- data/app/models/mission_control/jobs/current.rb +3 -0
- data/app/models/mission_control/jobs/job_progress.rb +7 -0
- data/app/models/mission_control/jobs/page.rb +48 -0
- data/app/models/mission_control/jobs/recurring_task.rb +25 -0
- data/app/models/mission_control/jobs/worker.rb +17 -0
- data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +14 -0
- data/app/views/layouts/mission_control/jobs/_flash.html.erb +9 -0
- data/app/views/layouts/mission_control/jobs/_navigation.html.erb +9 -0
- data/app/views/layouts/mission_control/jobs/application.html.erb +27 -0
- data/app/views/layouts/mission_control/jobs/application_selection/_applications.html.erb +13 -0
- data/app/views/layouts/mission_control/jobs/application_selection/_servers.html.erb +15 -0
- data/app/views/mission_control/jobs/jobs/_error_information.html.erb +22 -0
- data/app/views/mission_control/jobs/jobs/_filters.html.erb +55 -0
- data/app/views/mission_control/jobs/jobs/_general_information.html.erb +85 -0
- data/app/views/mission_control/jobs/jobs/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/_jobs_page.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/_progress_bar.html.erb +16 -0
- data/app/views/mission_control/jobs/jobs/_raw_data.html.erb +4 -0
- data/app/views/mission_control/jobs/jobs/_title.html.erb +22 -0
- data/app/views/mission_control/jobs/jobs/_toolbar.html.erb +18 -0
- data/app/views/mission_control/jobs/jobs/blocked/_actions.html.erb +3 -0
- data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +8 -0
- data/app/views/mission_control/jobs/jobs/failed/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/jobs/failed/_backtrace_toggle.html.erb +14 -0
- data/app/views/mission_control/jobs/jobs/failed/_job.html.erb +7 -0
- data/app/views/mission_control/jobs/jobs/finished/_job.html.erb +2 -0
- data/app/views/mission_control/jobs/jobs/in_progress/_actions.html.erb +6 -0
- data/app/views/mission_control/jobs/jobs/in_progress/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/index.html.erb +19 -0
- data/app/views/mission_control/jobs/jobs/scheduled/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/jobs/scheduled/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/show.html.erb +6 -0
- data/app/views/mission_control/jobs/queues/_actions.html.erb +7 -0
- data/app/views/mission_control/jobs/queues/_job.html.erb +15 -0
- data/app/views/mission_control/jobs/queues/_queue.html.erb +16 -0
- data/app/views/mission_control/jobs/queues/_queue_title.html.erb +17 -0
- data/app/views/mission_control/jobs/queues/index.html.erb +16 -0
- data/app/views/mission_control/jobs/queues/show.html.erb +25 -0
- data/app/views/mission_control/jobs/recurring_tasks/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/recurring_tasks/_general_information.html.erb +37 -0
- data/app/views/mission_control/jobs/recurring_tasks/_recurring_task.html.erb +22 -0
- data/app/views/mission_control/jobs/recurring_tasks/_title.html.erb +10 -0
- data/app/views/mission_control/jobs/recurring_tasks/index.html.erb +18 -0
- data/app/views/mission_control/jobs/recurring_tasks/show.html.erb +14 -0
- data/app/views/mission_control/jobs/shared/_job.html.erb +27 -0
- data/app/views/mission_control/jobs/shared/_jobs.html.erb +14 -0
- data/app/views/mission_control/jobs/shared/_pagination_toolbar.html.erb +7 -0
- data/app/views/mission_control/jobs/workers/_configuration.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/_raw_data.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/_title.html.erb +11 -0
- data/app/views/mission_control/jobs/workers/_worker.html.erb +21 -0
- data/app/views/mission_control/jobs/workers/_workers_page.html.erb +15 -0
- data/app/views/mission_control/jobs/workers/index.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/show.html.erb +14 -0
- data/config/importmap.rb +6 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20260828105144_create_mission_control_job_progresses.rb +16 -0
- data/lib/active_job/errors/invalid_operation.rb +5 -0
- data/lib/active_job/errors/job_not_found_error.rb +14 -0
- data/lib/active_job/errors/query_error.rb +5 -0
- data/lib/active_job/executing.rb +40 -0
- data/lib/active_job/execution_error.rb +8 -0
- data/lib/active_job/failed.rb +7 -0
- data/lib/active_job/job_proxy.rb +38 -0
- data/lib/active_job/jobs_relation.rb +318 -0
- data/lib/active_job/progress_reporting.rb +21 -0
- data/lib/active_job/querying.rb +44 -0
- data/lib/active_job/queue.rb +62 -0
- data/lib/active_job/queue_adapters/async_ext.rb +49 -0
- data/lib/active_job/queue_adapters/resque_ext.rb +317 -0
- data/lib/active_job/queue_adapters/solid_queue_ext/recurring_tasks.rb +52 -0
- data/lib/active_job/queue_adapters/solid_queue_ext/workers.rb +41 -0
- data/lib/active_job/queue_adapters/solid_queue_ext.rb +313 -0
- data/lib/active_job/queues.rb +33 -0
- data/lib/mission_control/jobs/adapter.rb +156 -0
- data/lib/mission_control/jobs/application.rb +20 -0
- data/lib/mission_control/jobs/applications.rb +8 -0
- data/lib/mission_control/jobs/arguments_filter.rb +24 -0
- data/lib/mission_control/jobs/authentication.rb +67 -0
- data/lib/mission_control/jobs/console/connect_to.rb +15 -0
- data/lib/mission_control/jobs/console/context.rb +11 -0
- data/lib/mission_control/jobs/console/jobs_help.rb +23 -0
- data/lib/mission_control/jobs/engine.rb +118 -0
- data/lib/mission_control/jobs/errors/incompatible_adapter.rb +2 -0
- data/lib/mission_control/jobs/errors/resource_not_found.rb +2 -0
- data/lib/mission_control/jobs/i18n_config.rb +17 -0
- data/lib/mission_control/jobs/identified_by_name.rb +18 -0
- data/lib/mission_control/jobs/identified_elements.rb +23 -0
- data/lib/mission_control/jobs/job_stopped_error.rb +5 -0
- data/lib/mission_control/jobs/progress/null_store.rb +25 -0
- data/lib/mission_control/jobs/progress/reporter.rb +63 -0
- data/lib/mission_control/jobs/progress/store.rb +72 -0
- data/lib/mission_control/jobs/progress.rb +17 -0
- data/lib/mission_control/jobs/server/recurring_tasks.rb +15 -0
- data/lib/mission_control/jobs/server/serializable.rb +24 -0
- data/lib/mission_control/jobs/server/workers.rb +13 -0
- data/lib/mission_control/jobs/server.rb +27 -0
- data/lib/mission_control/jobs/tasks.rb +8 -0
- data/lib/mission_control/jobs/version.rb +5 -0
- data/lib/mission_control/jobs/workers_relation.rb +78 -0
- data/lib/mission_control/jobs.rb +43 -0
- data/lib/resque/thread_safe_redis.rb +34 -0
- data/lib/solid_job_monitor.rb +6 -0
- metadata +527 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<table class="jobs <%= jobs_status %> table queues is-hoverable is-fullwidth">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th class="job-header">Job</th>
|
|
5
|
+
<% attribute_names_for_job_status(jobs_status).each do |attribute| %>
|
|
6
|
+
<th><%= attribute %></th>
|
|
7
|
+
<% end %>
|
|
8
|
+
</tr>
|
|
9
|
+
</thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
<%= render partial: "mission_control/jobs/jobs/job", collection: jobs_page.records %>
|
|
12
|
+
</tbody>
|
|
13
|
+
</table>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% progress = local_assigns[:progress]
|
|
2
|
+
job_id = local_assigns[:job]&.job_id || local_assigns[:job_id] %>
|
|
3
|
+
<% if progress %>
|
|
4
|
+
<div class="job-progress-wrap is-flex is-align-items-center"
|
|
5
|
+
data-controller="progress"
|
|
6
|
+
data-progress-url-value="<%= application_job_progress_path(@application, job_id) %>"
|
|
7
|
+
data-progress-percent-value="<%= progress.percent.to_i %>">
|
|
8
|
+
<div class="job-progress">
|
|
9
|
+
<div class="job-progress-bar" data-progress-target="bar"></div>
|
|
10
|
+
<span class="job-progress-label" data-progress-target="label"><%= progress.percent.to_i %>%</span>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="is-warning tag ml-2<%= " is-hidden" unless progress.stop_requested? %>" data-progress-target="stopping">Stopping…</div>
|
|
13
|
+
</div>
|
|
14
|
+
<% else %>
|
|
15
|
+
—
|
|
16
|
+
<% end %>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<h1 class="title">
|
|
2
|
+
<div class="level">
|
|
3
|
+
<div class="level-left">
|
|
4
|
+
<%= job_title(job) %>
|
|
5
|
+
<div class="<%= modifier_for_status(job.status) %> tag ml-4"><%= job.status %></div>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="level-right">
|
|
8
|
+
<% if job.failed? %>
|
|
9
|
+
<%= render "mission_control/jobs/jobs/failed/actions", job: job %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% if job.blocked? %>
|
|
12
|
+
<%= render "mission_control/jobs/jobs/blocked/actions", job: job %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% if job.scheduled? %>
|
|
15
|
+
<%= render "mission_control/jobs/jobs/scheduled/actions", job: job %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% if job.in_progress? %>
|
|
18
|
+
<%= render "mission_control/jobs/jobs/in_progress/actions", job: job %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</h1>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div class="buttons is-right level-right">
|
|
2
|
+
<% if active_filters? %>
|
|
3
|
+
<span class="mr-5">
|
|
4
|
+
<%= jobs_count %> jobs found
|
|
5
|
+
</span>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<% if jobs_status.failed? %>
|
|
9
|
+
<% target = active_filters? ? "selection" : "all" %>
|
|
10
|
+
|
|
11
|
+
<%= button_to "Discard #{target}", application_bulk_discards_path(@application, **jobs_filter_param),
|
|
12
|
+
method: :post, disabled: jobs_count == 0, class: "button is-danger is-light",
|
|
13
|
+
form: { data: { turbo_confirm: "This will delete #{jobs_count} jobs and can't be undone. Are you sure?" } } %>
|
|
14
|
+
<%= button_to "Retry #{target}", application_bulk_retries_path(@application, **jobs_filter_param),
|
|
15
|
+
method: :post, disabled: jobs_count == 0,
|
|
16
|
+
class: "button is-warning is-light mr-0" %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
|
2
|
+
<td>
|
|
3
|
+
<div class="is-family-monospace is-size-7"><%= job.blocked_by %></div>
|
|
4
|
+
<div class="has-text-grey is-size-7"><%= blocked_job_expiration(job) %></div>
|
|
5
|
+
</td>
|
|
6
|
+
<td class="pr-0">
|
|
7
|
+
<%= render "mission_control/jobs/jobs/blocked/actions", job: job %>
|
|
8
|
+
</td>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<div class="buttons is-right">
|
|
2
|
+
<%= button_to "Discard", application_job_discard_path(@application, job.job_id, params: jobs_filter_param), class: "button is-danger is-light mr-0",
|
|
3
|
+
form: { data: { turbo_confirm: "This will delete the job and can't be undone. Are you sure?" } } %>
|
|
4
|
+
<%= button_to "Retry", application_job_retry_path(@application, job.job_id, params: jobs_filter_param), class: "button is-warning is-light mr-0" %>
|
|
5
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%# locals: (application:, job:) %>
|
|
2
|
+
|
|
3
|
+
<div class="is-flex is-justify-content-flex-end mb-2 mr-2 backtrace-toggle-selector">
|
|
4
|
+
<div class="tabs is-toggle is-toggle-rounded is-small">
|
|
5
|
+
<ul>
|
|
6
|
+
<li class="<%= class_names('backtrace-clean-link', 'is-active' => clean_backtrace?) %>">
|
|
7
|
+
<%= link_to "Clean", application_job_path(application, job.job_id, clean_backtrace: true) %>
|
|
8
|
+
</li>
|
|
9
|
+
<li class="<%= class_names('backtrace-full-link', 'is-active' => !clean_backtrace?) %>">
|
|
10
|
+
<%= link_to "Full", application_job_path(application, job.job_id, clean_backtrace: false) %>
|
|
11
|
+
</li>
|
|
12
|
+
</ul>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<td>
|
|
2
|
+
<%= link_to failed_job_error(job), application_job_path(@application, job.job_id, anchor: "error") %>
|
|
3
|
+
<div class="has-text-grey"><%= time_distance_in_words_with_title(job.failed_at) %> ago</div>
|
|
4
|
+
</td>
|
|
5
|
+
<td class="pr-0">
|
|
6
|
+
<%= render "mission_control/jobs/jobs/failed/actions", job: job %>
|
|
7
|
+
</td>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<% if job_progress_enabled? %>
|
|
2
|
+
<div class="buttons is-right">
|
|
3
|
+
<%= button_to "Stop", application_job_stop_path(@application, job.job_id, params: jobs_filter_param), class: "button is-danger is-light mr-0",
|
|
4
|
+
form: { data: { turbo_confirm: "This requests the job to stop. It will stop the next time it reports progress or calls check_stopped!. Jobs that never do that will keep running. Are you sure?" } } %>
|
|
5
|
+
</div>
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
|
2
|
+
<td>
|
|
3
|
+
<% if job.worker_id %>
|
|
4
|
+
<%= link_to "worker #{job.worker_id}", application_worker_path(@application, job.worker_id) %>
|
|
5
|
+
<% else %>
|
|
6
|
+
—
|
|
7
|
+
<% end %>
|
|
8
|
+
</td>
|
|
9
|
+
<td><div class="has-text-grey"><%= job.started_at ? time_distance_in_words_with_title(job.started_at) : "(Finished)" %></div></td>
|
|
10
|
+
<td><%= render "mission_control/jobs/jobs/progress_bar", progress: progress_for(job), job: job %></td>
|
|
11
|
+
<td class="pr-0">
|
|
12
|
+
<%= render "mission_control/jobs/jobs/in_progress/actions", job: job %>
|
|
13
|
+
</td>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<% navigation(title: "#{jobs_status.titleize} jobs", section: "#{jobs_status}_jobs".to_sym) %>
|
|
2
|
+
|
|
3
|
+
<% if @jobs_page.empty? && !active_filters? %>
|
|
4
|
+
<%= blank_status_notice "There are no #{jobs_status.dasherize} jobs #{blank_status_emoji(jobs_status)}" %>
|
|
5
|
+
<% else %>
|
|
6
|
+
<div class="level is-flex-wrap-wrap">
|
|
7
|
+
<%= render "mission_control/jobs/jobs/filters", job_class_names: @job_class_names, queue_names: @queue_names %>
|
|
8
|
+
<%= render "mission_control/jobs/jobs/toolbar", jobs_count: @jobs_count %>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<% if @jobs_page.empty? %>
|
|
12
|
+
<%= blank_status_notice "No #{jobs_status.dasherize} jobs found with the given filters" %>
|
|
13
|
+
<% else %>
|
|
14
|
+
<%= render "mission_control/jobs/jobs/jobs_page", jobs_page: @jobs_page %>
|
|
15
|
+
|
|
16
|
+
<%= render "mission_control/jobs/shared/pagination_toolbar", page: @jobs_page, filter_param: jobs_filter_param %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<div class="buttons is-right">
|
|
2
|
+
<%= button_to "Run now", application_job_dispatch_path(@application, job.job_id), class: "button is-warning is-light mr-0" %>
|
|
3
|
+
<%= button_to "Discard", application_job_discard_path(@application, job.job_id), class: "button is-danger is-light mr-0",
|
|
4
|
+
form: { data: { turbo_confirm: "This will delete the job and can't be undone. Are you sure?" } } %>
|
|
5
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
|
2
|
+
<td>
|
|
3
|
+
<%= bidirectional_time_distance_in_words_with_title(job.scheduled_at) %>
|
|
4
|
+
<% if job.executions > 0 %>
|
|
5
|
+
<div class="is-warning tag ml-4"><%= job.executions.ordinalize %> retry</div>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% if job_delayed?(job) %>
|
|
8
|
+
<div class="is-danger tag ml-4">delayed</div>
|
|
9
|
+
<% end %>
|
|
10
|
+
</td>
|
|
11
|
+
<td class="pr-0">
|
|
12
|
+
<%= render "mission_control/jobs/jobs/scheduled/actions", job: job %>
|
|
13
|
+
</td>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<% navigation(title: "Job #{@job.job_id}", section: navigation_section_for_status(@job.status)) %>
|
|
2
|
+
|
|
3
|
+
<%= render "mission_control/jobs/jobs/title", job: @job %>
|
|
4
|
+
<%= render "mission_control/jobs/jobs/general_information", job: @job %>
|
|
5
|
+
<%= render "mission_control/jobs/jobs/error_information", job: @job %>
|
|
6
|
+
<%= render "mission_control/jobs/jobs/raw_data", job: @job %>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<div class="buttons is-right">
|
|
2
|
+
<% if queue.active? %>
|
|
3
|
+
<%= button_to "Pause", application_queue_pause_path(@application, queue.name), method: :post, class: "button is-success is-light mr-0" %>
|
|
4
|
+
<% else %>
|
|
5
|
+
<%= button_to "Resume", application_queue_pause_path(@application, queue.name), method: :delete, class: "button is-warning is-light mr-0" %>
|
|
6
|
+
<% end %>
|
|
7
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<tr class="job">
|
|
2
|
+
<td>
|
|
3
|
+
<%= link_to application_job_path(@application, job.job_id, filter: { queue_name: job.queue }) do %>
|
|
4
|
+
<%= job_title(job) %>
|
|
5
|
+
<% end %>
|
|
6
|
+
<div class="has-text-grey">Enqueued <%= time_distance_in_words_with_title(job.enqueued_at.to_datetime) %> ago</div>
|
|
7
|
+
</td>
|
|
8
|
+
<td>
|
|
9
|
+
<% if job.serialized_arguments.present? %>
|
|
10
|
+
<span class="is-family-monospace">
|
|
11
|
+
<%= job_arguments(job) %>
|
|
12
|
+
</span>
|
|
13
|
+
<% end %>
|
|
14
|
+
</td>
|
|
15
|
+
</tr>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<tr class="queue">
|
|
2
|
+
<td>
|
|
3
|
+
<%= link_to queue.name, application_queue_path(@application, queue) %>
|
|
4
|
+
<% if queue.paused? %>
|
|
5
|
+
<span class="ml-4 tag is-warning">Paused</span>
|
|
6
|
+
<% end %>
|
|
7
|
+
</td>
|
|
8
|
+
<td>
|
|
9
|
+
<%= queue.size %>
|
|
10
|
+
</td>
|
|
11
|
+
<td class="pr-0">
|
|
12
|
+
<% if queue_pausing_supported? %>
|
|
13
|
+
<%= render "mission_control/jobs/queues/actions", queue: queue %>
|
|
14
|
+
<% end %>
|
|
15
|
+
</td>
|
|
16
|
+
</tr>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<h1 class="title">
|
|
2
|
+
<div class="level">
|
|
3
|
+
<div class="level-left">
|
|
4
|
+
<%= queue.name %>
|
|
5
|
+
<% if queue.paused? %>
|
|
6
|
+
<span class="ml-4 tag is-warning">Paused</span>
|
|
7
|
+
<% end %>
|
|
8
|
+
</div>
|
|
9
|
+
<% if queue_pausing_supported? %>
|
|
10
|
+
<div class="level-right">
|
|
11
|
+
<%= render "mission_control/jobs/queues/actions", queue: queue %>
|
|
12
|
+
</div>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
15
|
+
</h1>
|
|
16
|
+
|
|
17
|
+
<h2 class="subtitle"><%= pluralize queue.size, "pending job" %></h2>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% navigation(title: "Queues", section: :queues) %>
|
|
2
|
+
|
|
3
|
+
<table class="queues table is-hoverable is-fullwidth">
|
|
4
|
+
<tbody>
|
|
5
|
+
<thead>
|
|
6
|
+
<tr>
|
|
7
|
+
<th>Queue</th>
|
|
8
|
+
<th>Pending jobs</th>
|
|
9
|
+
<th></th>
|
|
10
|
+
</tr>
|
|
11
|
+
</thead>
|
|
12
|
+
|
|
13
|
+
<%= render partial: "mission_control/jobs/queues/queue", collection: @queues %>
|
|
14
|
+
|
|
15
|
+
</tbody>
|
|
16
|
+
</table>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<% navigation(title: "Queue #{@queue.name}", section: :queues) %>
|
|
2
|
+
|
|
3
|
+
<%= render "mission_control/jobs/queues/queue_title", queue: @queue %>
|
|
4
|
+
|
|
5
|
+
<% if @jobs_page.empty? %>
|
|
6
|
+
<%= blank_status_notice "The queue is empty" %>
|
|
7
|
+
<% else %>
|
|
8
|
+
<table class="jobs table queues is-hoverable is-fullwidth">
|
|
9
|
+
<tbody>
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th class="job-header">Job</th>
|
|
13
|
+
<th></th>
|
|
14
|
+
</tr>
|
|
15
|
+
</thead>
|
|
16
|
+
|
|
17
|
+
<%= render partial: "mission_control/jobs/queues/job", collection: @jobs_page.records %>
|
|
18
|
+
|
|
19
|
+
</tbody>
|
|
20
|
+
</table>
|
|
21
|
+
|
|
22
|
+
<%= render "mission_control/jobs/shared/pagination_toolbar", page: @jobs_page, filter_param: {} %>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<table class="table">
|
|
2
|
+
<tbody>
|
|
3
|
+
<% if recurring_task.job_class_name.present? %>
|
|
4
|
+
<tr>
|
|
5
|
+
<th>Job class</th>
|
|
6
|
+
<td><%= recurring_task.job_class_name %></td>
|
|
7
|
+
</tr>
|
|
8
|
+
|
|
9
|
+
<tr>
|
|
10
|
+
<th>Arguments</th>
|
|
11
|
+
<td><div class="is-family-monospace"><%= recurring_task.arguments.join(",") %></div></td>
|
|
12
|
+
</tr>
|
|
13
|
+
<% elsif recurring_task.command.present? %>
|
|
14
|
+
<tr>
|
|
15
|
+
<th>Command</th>
|
|
16
|
+
<td><div class="is-family-monospace"><%= recurring_task.command %></div></td>
|
|
17
|
+
</tr>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<tr>
|
|
21
|
+
<th>Schedule</th>
|
|
22
|
+
<td><%= recurring_task.schedule %></td>
|
|
23
|
+
</tr>
|
|
24
|
+
<% if recurring_task.queue_name.present? %>
|
|
25
|
+
<tr>
|
|
26
|
+
<th>Queue</th>
|
|
27
|
+
<td><%= recurring_task.queue_name %></td>
|
|
28
|
+
</tr>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% if recurring_task.priority.present? %>
|
|
31
|
+
<tr>
|
|
32
|
+
<th>Priority</th>
|
|
33
|
+
<td><%= recurring_task.priority %></td>
|
|
34
|
+
</tr>
|
|
35
|
+
<% end %>
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<tr class="recurring_task">
|
|
2
|
+
<td>
|
|
3
|
+
<%= link_to recurring_task.id, application_recurring_task_path(@application, recurring_task.id) %>
|
|
4
|
+
</td>
|
|
5
|
+
<td>
|
|
6
|
+
<% if recurring_task.job_class_name.present? %>
|
|
7
|
+
<%= recurring_task.job_class_name %>
|
|
8
|
+
|
|
9
|
+
<% if recurring_task.arguments.present? %>
|
|
10
|
+
<div class="is-family-monospace"><%= recurring_task.arguments.join(",") %></div>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% elsif recurring_task.command.present? %>
|
|
13
|
+
<div class="is-family-monospace"><%= recurring_task.command %></div>
|
|
14
|
+
<% end %>
|
|
15
|
+
</td>
|
|
16
|
+
<td> <%= recurring_task.schedule %> </td>
|
|
17
|
+
<td><div class="has-text-grey"><%= recurring_task.last_enqueued_at ? bidirectional_time_distance_in_words_with_title(recurring_task.last_enqueued_at) : "Never" %></div></td>
|
|
18
|
+
<td class="next_time"><div class="has-text-grey"><%= bidirectional_time_distance_in_words_with_title(recurring_task.next_time) %></div></td>
|
|
19
|
+
<td class="pr-0">
|
|
20
|
+
<%= render "mission_control/jobs/recurring_tasks/actions", recurring_task: recurring_task %>
|
|
21
|
+
</td>
|
|
22
|
+
</tr>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<% navigation(title: "Recurring tasks", section: :recurring_tasks) %>
|
|
2
|
+
|
|
3
|
+
<table class="recurring-tasks table jobs is-hoverable is-fullwidth">
|
|
4
|
+
<tbody>
|
|
5
|
+
<thead>
|
|
6
|
+
<tr>
|
|
7
|
+
<th></th>
|
|
8
|
+
<th>Job</th>
|
|
9
|
+
<th>Schedule</th>
|
|
10
|
+
<th>Last enqueued</th>
|
|
11
|
+
<th>Next</th>
|
|
12
|
+
<th></th>
|
|
13
|
+
</tr>
|
|
14
|
+
</thead>
|
|
15
|
+
|
|
16
|
+
<%= render partial: "mission_control/jobs/recurring_tasks/recurring_task", collection: @recurring_tasks %>
|
|
17
|
+
</tbody>
|
|
18
|
+
</table>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<% navigation(title: @recurring_task.id, section: :recurring_tasks) %>
|
|
2
|
+
|
|
3
|
+
<%= render "mission_control/jobs/recurring_tasks/title", recurring_task: @recurring_task %>
|
|
4
|
+
<%= render "mission_control/jobs/recurring_tasks/general_information", recurring_task: @recurring_task %>
|
|
5
|
+
|
|
6
|
+
<% if @jobs_page.empty? %>
|
|
7
|
+
<%= blank_status_notice "No jobs found for this recurring task" %>
|
|
8
|
+
<% else %>
|
|
9
|
+
<h2 class="subtitle"><%= pluralize @recurring_task.jobs.count, "job" %></h2>
|
|
10
|
+
|
|
11
|
+
<%= render "mission_control/jobs/shared/jobs", jobs: @jobs_page.records %>
|
|
12
|
+
|
|
13
|
+
<%= render "mission_control/jobs/shared/pagination_toolbar", page: @jobs_page, filter_param: jobs_filter_param %>
|
|
14
|
+
<% end %>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<tr class="job">
|
|
2
|
+
<td>
|
|
3
|
+
<%= link_to application_job_path(@application, job.job_id, filter: { queue_name: job.queue }) do %>
|
|
4
|
+
<%= job_title(job) %>
|
|
5
|
+
<% end %>
|
|
6
|
+
<div class="has-text-grey">Enqueued <%= time_distance_in_words_with_title(job.enqueued_at.to_datetime) %> ago</div>
|
|
7
|
+
</td>
|
|
8
|
+
<td>
|
|
9
|
+
<% if job.serialized_arguments.present? %>
|
|
10
|
+
<span class="is-family-monospace">
|
|
11
|
+
<%= job_arguments(job) %>
|
|
12
|
+
</span>
|
|
13
|
+
<% end %>
|
|
14
|
+
</td>
|
|
15
|
+
|
|
16
|
+
<td>
|
|
17
|
+
<div class="has-text-grey">
|
|
18
|
+
<% if job.started_at %>
|
|
19
|
+
Running for <%= time_distance_in_words_with_title(job.started_at) %>
|
|
20
|
+
<% elsif job.finished_at %>
|
|
21
|
+
Finished <%= time_distance_in_words_with_title(job.finished_at) %> ago
|
|
22
|
+
<% else %>
|
|
23
|
+
Pending
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
26
|
+
</td>
|
|
27
|
+
</tr>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<table class="jobs table is-hoverable is-fullwidth">
|
|
2
|
+
<tbody>
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
<th class="job-header">Job</th>
|
|
6
|
+
<th></th>
|
|
7
|
+
<th class="duration-header"></th>
|
|
8
|
+
</tr>
|
|
9
|
+
</thead>
|
|
10
|
+
|
|
11
|
+
<%= render partial: "mission_control/jobs/shared/job", collection: jobs %>
|
|
12
|
+
|
|
13
|
+
</tbody>
|
|
14
|
+
</table>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<nav class="buttons is-right" role="navigation" aria-label="pagination">
|
|
2
|
+
<span class="mr-3"><%= page.index %> / <%= page.pages_count || "..." %></span>
|
|
3
|
+
<%= link_to "↞", url_for(page: 1, **filter_param), class: "pagination-previous", disabled: page.first? %>
|
|
4
|
+
<%= link_to "Previous page", url_for(page: page.previous_index, **filter_param), class: "pagination-previous", disabled: page.first? %>
|
|
5
|
+
<%= link_to "Next page", url_for(page: page.next_index, **filter_param), class: "pagination-next", disabled: page.last? %>
|
|
6
|
+
<%= link_to "↠", url_for(page: page.pages_count, **filter_param), class: "pagination-next", disabled: page.last? %>
|
|
7
|
+
</nav>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<tr class="worker">
|
|
2
|
+
<td>
|
|
3
|
+
<%= link_to "worker #{worker.id}", application_worker_path(@application, worker.id) %>
|
|
4
|
+
<br />
|
|
5
|
+
<%= worker.name %>
|
|
6
|
+
</td>
|
|
7
|
+
<td><%= worker.hostname %></td>
|
|
8
|
+
<td>
|
|
9
|
+
<% worker.jobs.each do |job| %>
|
|
10
|
+
<div>
|
|
11
|
+
<%= link_to job_title(job), application_job_path(@application, job.job_id) %>
|
|
12
|
+
|
|
13
|
+
<% if job.serialized_arguments.present? %>
|
|
14
|
+
<div class="is-family-monospace is-size-7"><%= job_arguments(job) %></div>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
18
|
+
</td>
|
|
19
|
+
|
|
20
|
+
<td><div class="has-text-grey"><%= time_distance_in_words_with_title(worker.last_heartbeat_at) %> ago</div></td>
|
|
21
|
+
</tr>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<table class="workers table jobs is-hoverable is-fullwidth">
|
|
2
|
+
<tbody>
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
<th>Worker</th>
|
|
6
|
+
<th>Hostname</th>
|
|
7
|
+
<th class="job-header">Jobs</th>
|
|
8
|
+
<th>Last heartbeat</th>
|
|
9
|
+
</tr>
|
|
10
|
+
</thead>
|
|
11
|
+
|
|
12
|
+
<%= render partial: "mission_control/jobs/workers/worker", collection: workers_page.records %>
|
|
13
|
+
|
|
14
|
+
</tbody>
|
|
15
|
+
</table>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<% navigation(title: "Worker #{@worker.id}", section: :workers) %>
|
|
2
|
+
|
|
3
|
+
<%= render "mission_control/jobs/workers/title", worker: @worker %>
|
|
4
|
+
<%= render "mission_control/jobs/workers/configuration", worker: @worker %>
|
|
5
|
+
|
|
6
|
+
<% if @worker.jobs.empty? %>
|
|
7
|
+
<%= blank_status_notice "This worker is idle" %>
|
|
8
|
+
<% else %>
|
|
9
|
+
<h2 class="subtitle">Running <%= pluralize @worker.jobs.size, "job" %></h2>
|
|
10
|
+
|
|
11
|
+
<%= render "mission_control/jobs/shared/jobs", jobs: @worker.jobs %>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<%= render "mission_control/jobs/workers/raw_data", worker: @worker %>
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
pin "application", to: "mission_control/jobs/application.js", preload: true
|
|
2
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
|
|
3
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
|
|
4
|
+
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
|
5
|
+
pin_all_from MissionControl::Jobs::Engine.root.join("app/javascript/mission_control/jobs/controllers"), under: "controllers", to: "mission_control/jobs/controllers"
|
|
6
|
+
pin_all_from MissionControl::Jobs::Engine.root.join("app/javascript/mission_control/jobs/helpers"), under: "helpers", to: "mission_control/jobs/helpers"
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
MissionControl::Jobs::Engine.routes.draw do
|
|
2
|
+
resources :applications, only: [] do
|
|
3
|
+
resources :queues, only: [ :index, :show ] do
|
|
4
|
+
scope module: :queues do
|
|
5
|
+
resource :pause, only: [ :create, :destroy ]
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
resources :jobs, only: :show do
|
|
10
|
+
resource :retry, only: :create
|
|
11
|
+
resource :discard, only: :create
|
|
12
|
+
resource :dispatch, only: :create
|
|
13
|
+
resource :stop, only: :create
|
|
14
|
+
resource :progress, only: :show
|
|
15
|
+
|
|
16
|
+
collection do
|
|
17
|
+
resource :bulk_retries, only: :create
|
|
18
|
+
resource :bulk_discards, only: :create
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
resources :jobs, only: :index, path: ":status/jobs"
|
|
23
|
+
|
|
24
|
+
resources :workers, only: [ :index, :show ]
|
|
25
|
+
resources :recurring_tasks, only: [ :index, :show, :update ]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Allow referencing urls without providing an application_id. It will default to the first one.
|
|
29
|
+
resources :queues, only: [ :index, :show ]
|
|
30
|
+
|
|
31
|
+
resources :jobs, only: :show
|
|
32
|
+
resources :jobs, only: :index, path: ":status/jobs"
|
|
33
|
+
|
|
34
|
+
root to: "queues#index"
|
|
35
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateMissionControlJobProgresses < ActiveRecord::Migration[7.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :mission_control_job_progresses do |t|
|
|
4
|
+
t.string :job_id, null: false
|
|
5
|
+
t.integer :current, default: 0, null: false
|
|
6
|
+
t.integer :total
|
|
7
|
+
t.integer :percent
|
|
8
|
+
t.string :message
|
|
9
|
+
t.boolean :stop_requested, default: false, null: false
|
|
10
|
+
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index :mission_control_job_progresses, :job_id, unique: true
|
|
15
|
+
end
|
|
16
|
+
end
|