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,16 @@
|
|
1
|
+
#header
|
2
|
+
%a#menu-toggle.btn.btn-default.btn-blank.first{href: ""}
|
3
|
+
%i.fa.fa-bars
|
4
|
+
Menu
|
5
|
+
|
6
|
+
= link_to main_app.root_path, class: 'btn btn-default btn-blank last' do
|
7
|
+
%i.fa.fa-reply
|
8
|
+
Back Home
|
9
|
+
|
10
|
+
.clearfix
|
11
|
+
|
12
|
+
.brand
|
13
|
+
= link_to root_path, class: 'text-center' do
|
14
|
+
%i.fa.fa-rocket
|
15
|
+
rocket
|
16
|
+
.color job
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#sidebar-wrapper
|
2
|
+
%ul.sidebar-nav
|
3
|
+
%li.sidebar-brand
|
4
|
+
%a#menu-close.menu-close
|
5
|
+
%i.fa.fa-times
|
6
|
+
|
7
|
+
.clearfix
|
8
|
+
|
9
|
+
%li
|
10
|
+
= link_to jobs_path, class: active_page(jobs_path) do
|
11
|
+
%i.fa.fa-home
|
12
|
+
Jobs
|
13
|
+
%li
|
14
|
+
= link_to workers_path, class: active_page(workers_path) do
|
15
|
+
%i.fa.fa-database
|
16
|
+
Workers
|
17
|
+
%li
|
18
|
+
= link_to running_jobs_path, class: active_page(running_jobs_path) do
|
19
|
+
%i.fa.fa-circle-o-notch
|
20
|
+
Running
|
@@ -0,0 +1,33 @@
|
|
1
|
+
.col-md-4.job-list
|
2
|
+
.filter.btn-group.btn-group-justified{ style: 'padding: 1px 1px 1px 1px;', data: { toggle: 'buttons' } }
|
3
|
+
- %w( queued running failed completed ).each do |state|
|
4
|
+
%label.btn.btn-default.state-toggle{ class: ('active' if @states.include?(state.to_s) || @states.empty?), title: state.capitalize}
|
5
|
+
= check_box_tag(state, state, (@states.include?(state.to_s) || @states.empty?))
|
6
|
+
%i.fa{class: job_state_icon(state.to_sym)}
|
7
|
+
.clearfix
|
8
|
+
.list
|
9
|
+
- if jobs.empty?
|
10
|
+
.card.inner
|
11
|
+
.title
|
12
|
+
.lead.text-center No Jobs Found.
|
13
|
+
- else
|
14
|
+
- jobs.each do |job|
|
15
|
+
= link_to job_path(id: job.id, states: @states), class: job_card_class(job) do
|
16
|
+
.inner
|
17
|
+
.title
|
18
|
+
.lead{class: job_selected_class(job, @job)}
|
19
|
+
%i.fa{class: job_state_icon(job.state), style: 'font-size: 75%;', title: job.state}
|
20
|
+
= job_title(job)
|
21
|
+
|
22
|
+
.description= job.description.try(:truncate, 50)
|
23
|
+
.info
|
24
|
+
.duration.pull-left
|
25
|
+
- if job.failed?
|
26
|
+
%i.fa.fa-bomb
|
27
|
+
failed
|
28
|
+
- else
|
29
|
+
%i.fa.fa-clock-o
|
30
|
+
= job.duration
|
31
|
+
- if job.running?
|
32
|
+
.progress.pull-right
|
33
|
+
.progress-bar{ style: "width: #{job.percent_complete}%;", title: "#{job.percent_complete}% percent complete."}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
- status = job.status(Time.zone)
|
2
|
+
.id
|
3
|
+
%label ID:
|
4
|
+
= status.delete('id')
|
5
|
+
.status-message
|
6
|
+
%label Description:
|
7
|
+
= status.delete('description')
|
8
|
+
.status-message
|
9
|
+
%label Duration:
|
10
|
+
= status.delete('duration')
|
11
|
+
.status-message
|
12
|
+
%label Record Count:
|
13
|
+
= status.delete('record_count')
|
14
|
+
.created_at
|
15
|
+
%label Created At:
|
16
|
+
= status.delete('created_at')
|
17
|
+
.status-message
|
18
|
+
%label Started At:
|
19
|
+
= status.delete('started_at')
|
20
|
+
.status-message
|
21
|
+
%label % Complete:
|
22
|
+
= status.delete('percent_complete')
|
23
|
+
|
24
|
+
- remaining = {}
|
25
|
+
- status.each_pair do |key, value|
|
26
|
+
.status-message
|
27
|
+
- if ['state', '_type'].include?(key)
|
28
|
+
- elsif value.kind_of?(Hash) || value.kind_of?(Array)
|
29
|
+
- remaining[key] = value
|
30
|
+
- else
|
31
|
+
%label= "#{key.to_s.titleize}:"
|
32
|
+
= value
|
33
|
+
.clearfix
|
34
|
+
|
35
|
+
- remaining.each_pair do |key, value|
|
36
|
+
.status-message
|
37
|
+
%label= "#{key.to_s.titleize}:"
|
38
|
+
%pre
|
39
|
+
%code= pretty_print_array_or_hash(value)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
.col-md-10.col-md-offset-1
|
2
|
+
.jumbotron
|
3
|
+
%h3 There are currently #{@jobs.count} jobs running.
|
4
|
+
#accordion.panel-group
|
5
|
+
- @jobs.each do |job|
|
6
|
+
.panel.panel-default
|
7
|
+
.panel-heading
|
8
|
+
%h4.panel-title
|
9
|
+
%a.collapsed{href: "##{job.id}", data: {toggle: 'collapse', parent: '#accordion'} }
|
10
|
+
%i.fa.fa-plus-square-o{ style: 'font-size: 80%;'}
|
11
|
+
= job_title(job)
|
12
|
+
.panel-collapse.collapse{ id: job.id }
|
13
|
+
.panel-body
|
14
|
+
.info
|
15
|
+
.progress
|
16
|
+
.progress-bar{ style: "width: #{job.percent_complete}%; min-width: 1em;", title: "#{job.percent_complete}% percent complete."}
|
17
|
+
- if job.kind_of?(RocketJob::SlicedJob)
|
18
|
+
.slice_count
|
19
|
+
%label Slices Running:
|
20
|
+
= job.input.find_all { |s| s.state == :running }.count
|
21
|
+
- else
|
22
|
+
.worker
|
23
|
+
%label Worker:
|
24
|
+
= job.worker_name
|
25
|
+
|
26
|
+
= render partial: 'status', locals: { job: job }
|
27
|
+
- if job.kind_of?(RocketJob::SlicedJob)
|
28
|
+
%table.table
|
29
|
+
%tr
|
30
|
+
%th worker
|
31
|
+
%th parameters
|
32
|
+
- job.input.each('state' => 'running') do |slice|
|
33
|
+
%tr
|
34
|
+
%td= slice.worker_name
|
35
|
+
%td
|
36
|
+
%pre
|
37
|
+
%code= pretty_print_array_or_hash(slice.to_a)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#queue
|
2
|
+
= render partial: 'list', locals: { jobs: @jobs }
|
3
|
+
|
4
|
+
.col-md-8.job-status
|
5
|
+
#job
|
6
|
+
.lead= job_title(@job)
|
7
|
+
|
8
|
+
.job-state.inline-block
|
9
|
+
.left state
|
10
|
+
.right{ class: job_class(@job) }
|
11
|
+
= @job.state
|
12
|
+
|
13
|
+
.priority.pull-right
|
14
|
+
= form_for(@job, as: :job, url: job_path(@job), html: {method: :patch}) do |f|
|
15
|
+
|
16
|
+
.input-group.spinner{ "data-id" => @job.id }
|
17
|
+
= f.text_field(:priority, class: 'form-control')
|
18
|
+
|
19
|
+
.input-group-btn-vertical
|
20
|
+
%button#increase_priority.btn.btn-default.up{ type: 'button' }
|
21
|
+
%i.fa.fa-caret-up
|
22
|
+
|
23
|
+
%button#decrease_priority.btn.btn-default.down{ type: 'button' }
|
24
|
+
%i.fa.fa-caret-down
|
25
|
+
|
26
|
+
= submit_tag "set", class: "btn btn-sm btn-primary priority-set"
|
27
|
+
|
28
|
+
.job-retry.btn-group.pull-right{ style: 'margin-right: 1em;' }
|
29
|
+
- valid_events = @job.aasm.events.collect{ |e| e.name }
|
30
|
+
- if valid_events.include?(:abort)
|
31
|
+
= link_to "abort", abort_job_path(@job), method: :patch, class: 'btn btn-default'
|
32
|
+
|
33
|
+
- if @job.completed? || @job.aborted?
|
34
|
+
= link_to "destroy", job_path(@job), method: :delete, class: 'btn btn-default'
|
35
|
+
|
36
|
+
- if valid_events.include?(:fail)
|
37
|
+
= link_to "fail", fail_job_path(@job), method: :patch, class: 'btn btn-default'
|
38
|
+
|
39
|
+
- if valid_events.include?(:pause)
|
40
|
+
= link_to "pause", pause_job_path(@job), method: :patch, class: 'btn btn-default'
|
41
|
+
|
42
|
+
- if valid_events.include?(:resume)
|
43
|
+
= link_to "resume", resume_job_path(@job), method: :patch, class: 'btn btn-default'
|
44
|
+
|
45
|
+
- if valid_events.include?(:retry)
|
46
|
+
= link_to "retry", retry_job_path(@job), method: :patch, class: 'btn btn-default'
|
47
|
+
|
48
|
+
.clearfix
|
49
|
+
|
50
|
+
= render partial: 'status', locals: { job: @job }
|
@@ -0,0 +1,9 @@
|
|
1
|
+
- if worker.stopping?
|
2
|
+
%p Worker is stopping...
|
3
|
+
= link_to "destroy", worker_path(worker), method: :delete, class: 'btn btn-danger'
|
4
|
+
-else
|
5
|
+
= link_to "stop", stop_worker_path(worker), method: :patch, class: 'btn btn-danger'
|
6
|
+
- if worker.paused?
|
7
|
+
= link_to "resume", resume_worker_path(worker), method: :patch, class: 'btn btn-default'
|
8
|
+
- else
|
9
|
+
= link_to "pause", pause_worker_path(worker), method: :patch, class: 'btn btn-default'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#workers
|
2
|
+
- if @workers.present?
|
3
|
+
.col-lg-1
|
4
|
+
.dropdown
|
5
|
+
%button.btn.btn-primary.dropdown-toggle{ data: {toggle: 'dropdown'} }
|
6
|
+
Actions
|
7
|
+
%span.caret
|
8
|
+
%ul.dropdown-menu
|
9
|
+
%li= link_to("Stop All", update_all_workers_path(worker_action: :stop), method: :patch)
|
10
|
+
%li= link_to("Pause All", update_all_workers_path(worker_action: :pause), method: :patch)
|
11
|
+
%li= link_to("Resume All", update_all_workers_path(worker_action: :resume), method: :patch)
|
12
|
+
|
13
|
+
.grid
|
14
|
+
- @workers.each do |worker|
|
15
|
+
.card.col-xs-12.col-sm-6.col-md-3.col-lg-3
|
16
|
+
.inner.callout-top{ class: worker_card_class(worker) }
|
17
|
+
.icon
|
18
|
+
%i.fa.fa-database
|
19
|
+
|
20
|
+
.title
|
21
|
+
%h3
|
22
|
+
= worker.name
|
23
|
+
|
24
|
+
.state
|
25
|
+
%b State:
|
26
|
+
= worker.state
|
27
|
+
|
28
|
+
.threads.max_threads
|
29
|
+
%b Max Threads:
|
30
|
+
= worker.max_threads
|
31
|
+
|
32
|
+
.threads.current_threads
|
33
|
+
%b Current Threads:
|
34
|
+
= worker.heartbeat.current_threads.present? ? worker.heartbeat.current_threads : 0
|
35
|
+
|
36
|
+
.time
|
37
|
+
%b Started:
|
38
|
+
= worker.started_at.strftime("%b, %d %Y at %l:%M %p")
|
39
|
+
|
40
|
+
.actions
|
41
|
+
= render partial: 'actions', locals: { worker: worker }
|
42
|
+
|
43
|
+
- else
|
44
|
+
.no-workers
|
45
|
+
There are currently no workers.
|
46
|
+
%br
|
47
|
+
You can start a worker with the following command:
|
48
|
+
|
49
|
+
.code-block.col-md-6.col-md-offset-3
|
50
|
+
%pre
|
51
|
+
%code.language-bash bin/rocketjob
|
@@ -0,0 +1,57 @@
|
|
1
|
+
en:
|
2
|
+
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
|
3
|
+
datetime:
|
4
|
+
distance_in_words:
|
5
|
+
half_a_minute: "half a minute"
|
6
|
+
less_than_x_seconds:
|
7
|
+
one: "1 second" # default was: "less than 1 second"
|
8
|
+
other: "%{count} seconds" # default was: "less than %{count} seconds"
|
9
|
+
x_seconds:
|
10
|
+
one: "1 second"
|
11
|
+
other: "%{count} seconds"
|
12
|
+
less_than_x_minutes:
|
13
|
+
one: "a minute" # default was: "less than a minute"
|
14
|
+
other: "%{count} minutes" # default was: "less than %{count} minutes"
|
15
|
+
x_minutes:
|
16
|
+
one: "1 minute"
|
17
|
+
other: "%{count} minutes"
|
18
|
+
about_x_hours:
|
19
|
+
one: "1 hour" # default was: "about 1 hour"
|
20
|
+
other: "%{count} hours" # default was: "about %{count} hours"
|
21
|
+
x_days:
|
22
|
+
one: "1 day"
|
23
|
+
other: "%{count} days"
|
24
|
+
about_x_months:
|
25
|
+
one: "1 month" # default was: "about 1 month"
|
26
|
+
other: "%{count} months" # default was: "about %{count} months"
|
27
|
+
x_months:
|
28
|
+
one: "1 month"
|
29
|
+
other: "%{count} months"
|
30
|
+
about_x_years:
|
31
|
+
one: "1 year" # default was: "about 1 year"
|
32
|
+
other: "%{count} years" # default was: "about %{count} years"
|
33
|
+
over_x_years:
|
34
|
+
one: "1 year" # default was: "over 1 year"
|
35
|
+
other: "%{count} years" # default was: "over %{count} years"
|
36
|
+
almost_x_years:
|
37
|
+
one: "1 year" # default was: "almost 1 year"
|
38
|
+
other: "%{count} years" # default was: "almost %{count} years"
|
39
|
+
worker:
|
40
|
+
stop:
|
41
|
+
success: "Worker successfully stopped."
|
42
|
+
failure: "Error attempting to stop worker."
|
43
|
+
destroy:
|
44
|
+
success: "Worker successfully destroyed."
|
45
|
+
failure: "Error attempting to destroy worker."
|
46
|
+
pause:
|
47
|
+
success: "Worker successfully paused."
|
48
|
+
failure: "Error attempting to pause worker."
|
49
|
+
resume:
|
50
|
+
success: "Worker successfully resumed."
|
51
|
+
failure: "Error attempting to resume worker."
|
52
|
+
update_all:
|
53
|
+
success: "Workers have been %{worker_action}."
|
54
|
+
invalid: "Action not allowed."
|
55
|
+
job:
|
56
|
+
find:
|
57
|
+
:failure: "Could not find job with id: %{id}!"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
RocketJobMissionControl::Engine.routes.draw do
|
2
|
+
|
3
|
+
resources :jobs, only: [:index, :show, :update, :destroy] do
|
4
|
+
member do
|
5
|
+
patch :abort
|
6
|
+
patch :fail
|
7
|
+
patch :pause
|
8
|
+
patch :resume
|
9
|
+
patch :retry
|
10
|
+
end
|
11
|
+
collection do
|
12
|
+
get :running
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
resources :workers, only: [:index, :destroy] do
|
17
|
+
member do
|
18
|
+
patch :stop
|
19
|
+
patch :pause
|
20
|
+
patch :resume
|
21
|
+
end
|
22
|
+
collection do
|
23
|
+
patch :update_all
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
root to: "jobs#index"
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RocketJobMissionControl
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace RocketJobMissionControl
|
4
|
+
|
5
|
+
require 'rocketjob'
|
6
|
+
require 'jquery-rails'
|
7
|
+
require 'haml'
|
8
|
+
require 'mongo'
|
9
|
+
require 'mongo_mapper'
|
10
|
+
require 'mongo_ha'
|
11
|
+
require 'bootstrap-sass'
|
12
|
+
require 'sass-rails'
|
13
|
+
require 'coffee-rails'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module RocketJobMissionControl
|
4
|
+
RSpec.describe ApplicationController, type: :controller do
|
5
|
+
controller do
|
6
|
+
def index
|
7
|
+
@time_zone = Time.zone
|
8
|
+
|
9
|
+
render text: 'Time Zoned'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#with_time_zone" do
|
14
|
+
let(:session_params) { {} }
|
15
|
+
let(:expected_time_zone) { 'UTC' }
|
16
|
+
|
17
|
+
before do
|
18
|
+
get :index, {}, session_params
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with a session present" do
|
22
|
+
context "that contains a time zone" do
|
23
|
+
let(:expected_time_zone) { 'America/Los_Angeles' }
|
24
|
+
let(:session_params) { {'time_zone' => expected_time_zone} }
|
25
|
+
|
26
|
+
it "sets the time zone correctly" do
|
27
|
+
expect(assigns(:time_zone).name).to eq(expected_time_zone)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'that does not contain a time zone' do
|
32
|
+
let(:session_params) { { 'user_id' => '42' } }
|
33
|
+
|
34
|
+
it 'sets the time zone correctly' do
|
35
|
+
expect(assigns(:time_zone).name).to eq(expected_time_zone)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with no session present" do
|
41
|
+
it "sets the time zone correctly" do
|
42
|
+
expect(assigns(:time_zone).name).to eq(expected_time_zone)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module RocketJobMissionControl
|
4
|
+
RSpec.shared_examples "a jobs update controller" do
|
5
|
+
describe "with an invalid job id" do
|
6
|
+
before do
|
7
|
+
allow(RocketJob::Job).to receive(:find).and_return(nil)
|
8
|
+
do_action
|
9
|
+
end
|
10
|
+
|
11
|
+
it "redirects" do
|
12
|
+
expect(response).to redirect_to(jobs_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "adds a flash alert message" do
|
16
|
+
expect(flash[:alert]).to eq(I18n.t(:failure, scope: [:job, :find], id: 42))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.describe JobsController do
|
22
|
+
routes { Engine.routes }
|
23
|
+
|
24
|
+
[:pause, :resume, :abort, :retry, :fail].each do |state|
|
25
|
+
describe "PATCH ##{state}" do
|
26
|
+
it_behaves_like "a jobs update controller" do
|
27
|
+
let(:do_action) { patch state, id: 42, job: {id: 42, priority: 12} }
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "with a valid job id" do
|
31
|
+
let(:job) { spy(id: 42, to_param: 42) }
|
32
|
+
|
33
|
+
before do
|
34
|
+
allow(RocketJob::Job).to receive(:find).and_return(job)
|
35
|
+
patch state, id: 42, job: {id: 42, priority: 12}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "redirects to the job" do
|
39
|
+
expect(response).to redirect_to(job_path(42))
|
40
|
+
end
|
41
|
+
|
42
|
+
it "transitions the job" do
|
43
|
+
expect(job).to have_received("#{state}!".to_sym)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "PATCH #update" do
|
50
|
+
it_behaves_like "a jobs update controller" do
|
51
|
+
let(:do_action) { patch :update, id: 42, job: {id: 42, priority: 12} }
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "with a valid job id" do
|
55
|
+
let(:job) { spy(id: 42, to_param: 42) }
|
56
|
+
|
57
|
+
before do
|
58
|
+
allow(RocketJob::Job).to receive(:find).and_return(job)
|
59
|
+
patch :update, id: 42, job: {id: 42, priority: 12}
|
60
|
+
end
|
61
|
+
|
62
|
+
it "redirects to the job" do
|
63
|
+
expect(response).to redirect_to(job_path(42))
|
64
|
+
end
|
65
|
+
|
66
|
+
it "updates the job correctly" do
|
67
|
+
expect(job).to have_received(:update_attributes!).with('priority' => '12')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "GET #show" do
|
73
|
+
let(:result) { spy(sort: []) }
|
74
|
+
|
75
|
+
before do
|
76
|
+
allow(RocketJob::Job).to receive(:limit).and_return(result)
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "with an invalid job id" do
|
80
|
+
before do
|
81
|
+
allow(RocketJob::Job).to receive(:find).and_return(nil)
|
82
|
+
get :show, id: 42
|
83
|
+
end
|
84
|
+
|
85
|
+
it "redirects" do
|
86
|
+
expect(response).to redirect_to(jobs_path)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "adds a flash alert message" do
|
90
|
+
expect(flash[:alert]).to eq(I18n.t(:failure, scope: [:job, :find], id: 42))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "with a valid job id" do
|
95
|
+
before do
|
96
|
+
allow(RocketJob::Job).to receive(:find).and_return('job')
|
97
|
+
get :show, id: 42
|
98
|
+
end
|
99
|
+
|
100
|
+
it "succeeds" do
|
101
|
+
expect(response.status).to be(200)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "assigns the job" do
|
105
|
+
expect(assigns(:job)).to be_present
|
106
|
+
end
|
107
|
+
|
108
|
+
it "assigns the jobs" do
|
109
|
+
expect(assigns(:jobs)).to eq([])
|
110
|
+
end
|
111
|
+
|
112
|
+
it "grabs a sorted list of rocket jobs" do
|
113
|
+
expect(result).to have_received(:sort).with(created_at: :desc)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "GET #running" do
|
119
|
+
before do
|
120
|
+
allow(RocketJob::Job).to receive(:where).and_return([])
|
121
|
+
get :running
|
122
|
+
end
|
123
|
+
|
124
|
+
it { expect(response.status).to be(200) }
|
125
|
+
|
126
|
+
it "queries for running jobs" do
|
127
|
+
expect(RocketJob::Job).to have_received(:where).with(state: 'running')
|
128
|
+
end
|
129
|
+
|
130
|
+
it "returns expected jobs" do
|
131
|
+
expect(assigns[:jobs]).to eq([])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "GET #index" do
|
136
|
+
describe "with no jobs" do
|
137
|
+
let(:result) { spy(sort: []) }
|
138
|
+
|
139
|
+
before do
|
140
|
+
allow(RocketJob::Job).to receive(:limit).and_return(result)
|
141
|
+
get :index
|
142
|
+
end
|
143
|
+
|
144
|
+
it "succeeds" do
|
145
|
+
expect(response.status).to be(200)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "grabs a sorted list of rocket jobs" do
|
149
|
+
expect(result).to have_received(:sort).with(created_at: :desc)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "returns no jobs" do
|
153
|
+
expect(assigns(:jobs)).to eq([])
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "with jobs" do
|
158
|
+
let(:result) { spy(sort: jobs) }
|
159
|
+
let(:jobs) { ['fake_job1', 'fake_job2'] }
|
160
|
+
|
161
|
+
before do
|
162
|
+
allow(RocketJob::Job).to receive(:limit).and_return(result)
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "with no parameters" do
|
166
|
+
before { get :index }
|
167
|
+
|
168
|
+
it "succeeds" do
|
169
|
+
expect(response.status).to be(200)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "grabs a sorted list of rocket jobs" do
|
173
|
+
expect(result).to have_received(:sort).with(created_at: :desc)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "returns the jobs" do
|
177
|
+
expect(assigns(:jobs)).to match_array(jobs)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "with a state filter" do
|
182
|
+
before { get :index, states: states}
|
183
|
+
|
184
|
+
context "that is empty" do
|
185
|
+
let(:states) { [] }
|
186
|
+
|
187
|
+
it { expect(response.status).to be(200) }
|
188
|
+
|
189
|
+
it "grabs a sorted list of rocket jobs" do
|
190
|
+
expect(result).to have_received(:sort).with(created_at: :desc)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "returns the jobs" do
|
194
|
+
expect(assigns(:jobs)).to match_array(jobs)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "with a state" do
|
199
|
+
let(:query_spy) { spy(where: jobs) }
|
200
|
+
let(:result) { spy(sort: query_spy) }
|
201
|
+
let(:states) { ['completed', 'running'] }
|
202
|
+
|
203
|
+
it { expect(response.status).to be(200) }
|
204
|
+
|
205
|
+
it "grabs a filtered list of rocket jobs" do
|
206
|
+
expect(query_spy).to have_received(:where).with(state: states)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "returns the jobs" do
|
210
|
+
expect(assigns(:jobs)).to match_array(jobs)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|