kuroko2 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/app/controllers/kuroko2/execution_histories_controller.rb +73 -0
- data/app/controllers/kuroko2/workers_controller.rb +17 -0
- data/app/models/kuroko2/execution_history.rb +8 -0
- data/app/views/kuroko2/execution_histories/dataset.json.jbuilder +10 -0
- data/app/views/kuroko2/execution_histories/index.html.slim +39 -0
- data/app/views/kuroko2/execution_histories/timeline.html.slim +33 -0
- data/app/views/kuroko2/workers/index.html.slim +26 -4
- data/bin/cleanup_old_execution_histories.rb +9 -0
- data/config/routes.rb +6 -1
- data/db/migrate/031_add_suspended_to_workers.rb +6 -0
- data/db/migrate/031_create_execution_histories.rb +19 -0
- data/lib/autoload/kuroko2/command/executor.rb +2 -1
- data/lib/autoload/kuroko2/command/shell.rb +2 -0
- data/lib/autoload/kuroko2/workflow/notifier/concerns/chat_message_builder.rb +3 -3
- data/lib/autoload/kuroko2/workflow/task/execute.rb +11 -0
- data/lib/kuroko2/version.rb +1 -1
- data/spec/command/shell_spec.rb +10 -0
- data/spec/controllers/execution_histories_controller_spec.rb +177 -0
- data/spec/controllers/workers_controller_spec.rb +14 -0
- data/spec/dummy/db/schema.rb +17 -1
- data/spec/factories/execution_history_factory.rb +15 -0
- data/spec/factories/worker_factory.rb +1 -0
- data/spec/features/execution_histories_spec.rb +36 -0
- data/spec/features/workers_spec.rb +33 -0
- data/spec/models/execution_history_spec.rb +4 -0
- data/spec/workflow/task/execute_spec.rb +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d66f084f8f4eabb9f6a0c0b26dad87b4009a4f97
|
4
|
+
data.tar.gz: 0256ee9b074d99821adbd5c1a8bf3bb280c29574
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4412638419a0984cde3aaf825f92b95a4e65eae33f660968635440decb04362b659b06a7a8aa0b18796a3f00dd551fa09a553d92fbe195edcda806f642d1fcf2
|
7
|
+
data.tar.gz: 0cb2e9266689d1636c67b5e7eae2ef56556d1b9188b0e2e56abd3c1fff739de7c041feb3ea98a8afe3382f1d0813c8da773773125aed2d244076a693be8d8884
|
data/README.md
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
class Kuroko2::ExecutionHistoriesController < Kuroko2::ApplicationController
|
2
|
+
def index
|
3
|
+
@histories = histories.page(params[:page])
|
4
|
+
end
|
5
|
+
|
6
|
+
def timeline
|
7
|
+
end
|
8
|
+
|
9
|
+
def dataset
|
10
|
+
set_period
|
11
|
+
@histories = histories.where('started_at < ?', @end_at).where('finished_at > ?', @start_at)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def query_params
|
17
|
+
params.permit(:queue, :hostname)
|
18
|
+
end
|
19
|
+
|
20
|
+
def histories
|
21
|
+
histories = Kuroko2::ExecutionHistory.ordered.includes(:job_definition, :job_instance)
|
22
|
+
|
23
|
+
queue = query_params[:queue]
|
24
|
+
histories = histories.where(queue: queue) if queue.present?
|
25
|
+
|
26
|
+
hostname = query_params[:hostname]
|
27
|
+
histories = histories.where(hostname: hostname) if hostname.present?
|
28
|
+
|
29
|
+
histories
|
30
|
+
end
|
31
|
+
|
32
|
+
def period_params
|
33
|
+
params.permit(:period, :end_at, :start_at)
|
34
|
+
end
|
35
|
+
|
36
|
+
def end_at
|
37
|
+
if period_params[:end_at].present?
|
38
|
+
begin
|
39
|
+
return period_params[:end_at].to_datetime
|
40
|
+
rescue ArgumentError
|
41
|
+
# do nothing
|
42
|
+
end
|
43
|
+
end
|
44
|
+
Time.current
|
45
|
+
end
|
46
|
+
|
47
|
+
def start_at
|
48
|
+
if period_params[:start_at].present?
|
49
|
+
begin
|
50
|
+
return period_params[:start_at].to_datetime
|
51
|
+
rescue ArgumentError
|
52
|
+
# do nothing
|
53
|
+
end
|
54
|
+
end
|
55
|
+
case period_params[:period]
|
56
|
+
when /\A(\d+)m\z/
|
57
|
+
$1.to_i.minutes.ago(@end_at)
|
58
|
+
when /\A(\d+)h\z/
|
59
|
+
$1.to_i.hours.ago(@end_at)
|
60
|
+
when /\A(\d+)d\z/
|
61
|
+
$1.to_i.days.ago(@end_at)
|
62
|
+
when /\A(\d+)w\z/
|
63
|
+
$1.to_i.weeks.ago(@end_at)
|
64
|
+
else
|
65
|
+
1.hour.ago(@end_at)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_period
|
70
|
+
@end_at = end_at
|
71
|
+
@start_at = start_at
|
72
|
+
end
|
73
|
+
end
|
@@ -1,5 +1,22 @@
|
|
1
1
|
class Kuroko2::WorkersController < Kuroko2::ApplicationController
|
2
|
+
before_action :set_worker, only: %i(update)
|
3
|
+
|
2
4
|
def index
|
3
5
|
@workers = Kuroko2::Worker.ordered.all
|
4
6
|
end
|
7
|
+
|
8
|
+
def update
|
9
|
+
@worker.update_attributes(worker_params)
|
10
|
+
redirect_to workers_path
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def set_worker
|
16
|
+
@worker = Kuroko2::Worker.find(params[:id])
|
17
|
+
end
|
18
|
+
|
19
|
+
def worker_params
|
20
|
+
params.permit(:suspended)
|
21
|
+
end
|
5
22
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
json.start @start_at.strftime('%Y-%m-%d %H:%M:%S')
|
2
|
+
json.end @end_at.strftime('%Y-%m-%d %H:%M:%S')
|
3
|
+
json.data do
|
4
|
+
json.array! @histories do |history|
|
5
|
+
json.id history.id
|
6
|
+
json.content "<a href='#{job_definition_job_instance_path(history.job_definition, history.job_instance)}'>##{history.job_definition.id} #{h(history.job_definition.name)}</a>"
|
7
|
+
json.start history.started_at.strftime('%Y-%m-%d %H:%M:%S')
|
8
|
+
json.end history.finished_at.strftime('%Y-%m-%d %H:%M:%S')
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
- content_for :title, 'Execution Histories'
|
2
|
+
- content_for :content_title do
|
3
|
+
<i class="fa fa-history"></i> Execution Histories
|
4
|
+
- @scope = @histories
|
5
|
+
|
6
|
+
.box#execution-histories
|
7
|
+
.box-header
|
8
|
+
.row
|
9
|
+
.col-md-9
|
10
|
+
h3.box-title Execution Histories
|
11
|
+
.col-md-3.right-button
|
12
|
+
= link_to raw('<i class="fa fa-clock-o"></i> Show Timeline'), timeline_execution_histories_path(params.permit(:queue, :hostname)), class: 'btn btn-default btn-small btn-block js-to-timeline'
|
13
|
+
- if @histories.empty?
|
14
|
+
.box-body
|
15
|
+
.text-muted.well.well-sm.no-shadow There are no execution histories yet.
|
16
|
+
- else
|
17
|
+
.box-body.table-responsive
|
18
|
+
table.table.table-hover
|
19
|
+
thead
|
20
|
+
tr
|
21
|
+
th.col-md-2 Hostname
|
22
|
+
th.col-md-1 WID
|
23
|
+
th.col-md-2 Queue
|
24
|
+
th.col-md-2 Job
|
25
|
+
th.col-md-2 Command
|
26
|
+
th.col-md-2 Started at
|
27
|
+
th.col-md-1 Elapsed Time
|
28
|
+
tbody
|
29
|
+
- for history in @histories
|
30
|
+
tr
|
31
|
+
td= history.hostname
|
32
|
+
td= history.worker_id
|
33
|
+
td= history.queue
|
34
|
+
td.no-decorate= link_to "##{history.job_definition.id} #{history.job_definition.name}", job_definition_job_instance_path(history.job_definition, history.job_instance)
|
35
|
+
td= history.shell
|
36
|
+
td= l(history.started_at, format: :short)
|
37
|
+
td= distance_of_time(history.started_at, history.finished_at)
|
38
|
+
.box-footer#pagination
|
39
|
+
= paginate @histories, theme: 'list'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
- content_for :title, "Execution Timeline"
|
2
|
+
- content_for :content_title do
|
3
|
+
<i class="fa fa-clock-o"></i> Execution Timeline
|
4
|
+
|
5
|
+
.row
|
6
|
+
.col-md-12
|
7
|
+
.box
|
8
|
+
.box-header
|
9
|
+
nav class="navbar navbar-default"
|
10
|
+
div class="container-fluid"
|
11
|
+
div class="navbar-header"
|
12
|
+
a class="navbar-brand" href="#" Period
|
13
|
+
div class="" id="period-nav"
|
14
|
+
ul class="nav navbar-nav"
|
15
|
+
li class="#{params[:period] == '30m' ? 'active' : ''}"
|
16
|
+
= link_to '30 minutes', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '30m'))
|
17
|
+
li class="#{params[:period] == '1h' || !params[:period] ? 'active' : ''}"
|
18
|
+
= link_to '1 hour', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '1h'))
|
19
|
+
li class="#{params[:period] == '3h' ? 'active' : ''}"
|
20
|
+
= link_to '3 hours', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '3h'))
|
21
|
+
li class="#{params[:period] == '6h' ? 'active' : ''}"
|
22
|
+
= link_to '6 hours', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '6h'))
|
23
|
+
li class="#{params[:period] == '12h' ? 'active' : ''}"
|
24
|
+
= link_to '12 hours', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '12h'))
|
25
|
+
li class="#{params[:period] == '1d' ? 'active' : ''}"
|
26
|
+
= link_to '1 day', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '1d'))
|
27
|
+
li class="#{params[:period] == '3d' ? 'active' : ''}"
|
28
|
+
= link_to '3 days', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '3d'))
|
29
|
+
li class="#{params[:period] == '1w' ? 'active' : ''}"
|
30
|
+
= link_to '1 week', timeline_execution_histories_path(params.permit(:queue, :hostname, :end_at).merge(period: '1w'))
|
31
|
+
|
32
|
+
.box-body
|
33
|
+
div#timeline data-dataset-path="#{dataset_execution_histories_path(params.permit(:queue, :hostname, :start_at, :end_at, :period))}"
|
@@ -13,15 +13,18 @@
|
|
13
13
|
th.col-md-1 WID
|
14
14
|
th.col-md-2 Queue
|
15
15
|
th.col-md-1 Status
|
16
|
-
th.col-md-
|
16
|
+
th.col-md-4 Execution
|
17
|
+
th.col-md-1
|
17
18
|
th.col-md-1
|
18
19
|
- for worker in @workers
|
19
20
|
tr
|
20
|
-
td= worker.hostname
|
21
|
+
td.no-decorate= link_to worker.hostname, execution_histories_path(hostname: worker.hostname)
|
21
22
|
td= worker.worker_id
|
22
|
-
td= worker.queue
|
23
|
+
td.no-decorate= link_to worker.queue, execution_histories_path(queue: worker.queue)
|
23
24
|
td
|
24
|
-
- if worker.
|
25
|
+
- if worker.suspended
|
26
|
+
span.label.label-warning SUSPENDED
|
27
|
+
- elsif worker.working
|
25
28
|
span.label.label-primary WORKING
|
26
29
|
- else
|
27
30
|
'
|
@@ -39,3 +42,22 @@
|
|
39
42
|
role: 'button', class: 'btn btn-sm btn-default')
|
40
43
|
- else
|
41
44
|
'
|
45
|
+
td
|
46
|
+
- if !worker.suspendable
|
47
|
+
'
|
48
|
+
- elsif !worker.suspended
|
49
|
+
= button_to('Suspend',
|
50
|
+
worker_path(worker),
|
51
|
+
method: :patch,
|
52
|
+
params: { suspended: true },
|
53
|
+
role: 'button',
|
54
|
+
class: 'btn btn-xs btn-default',
|
55
|
+
data: { confirm: 'Continue suspending the worker?' })
|
56
|
+
- else
|
57
|
+
= button_to('Unsuspend',
|
58
|
+
worker_path(worker),
|
59
|
+
method: :patch,
|
60
|
+
params: { suspended: false },
|
61
|
+
role: 'button',
|
62
|
+
class: 'btn btn-xs btn-default',
|
63
|
+
data: { confirm: 'Continue unsuspending the worker?' })
|
data/config/routes.rb
CHANGED
@@ -22,7 +22,7 @@ Kuroko2::Engine.routes.draw do
|
|
22
22
|
get 'page/:page', action: :index, on: :collection, as: 'paged'
|
23
23
|
end
|
24
24
|
|
25
|
-
resources :workers, only:
|
25
|
+
resources :workers, only: %i(index update)
|
26
26
|
resources :job_instances, path: 'instances', only: %w() do
|
27
27
|
get :working, action: :working, on: :collection
|
28
28
|
end
|
@@ -32,6 +32,11 @@ Kuroko2::Engine.routes.draw do
|
|
32
32
|
get :dataset, action: :dataset, on: :collection, defaults: { format: 'json' }
|
33
33
|
end
|
34
34
|
|
35
|
+
resources :execution_histories, only: :index do
|
36
|
+
get :timeline, action: :timeline, on: :collection
|
37
|
+
get 'timeline/dataset', action: :dataset, on: :collection, defaults: { format: 'json' }, as: 'dataset'
|
38
|
+
end
|
39
|
+
|
35
40
|
get '/sign_in', to: 'sessions#new', as: 'sign_in'
|
36
41
|
delete '/sign_out', to: 'sessions#destroy', as: 'sign_out'
|
37
42
|
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class AddSuspendedToWorkers < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
add_column :workers, :suspendable, :boolean, default: false, null: false, after: :execution_id
|
4
|
+
add_column :workers, :suspended, :boolean, default: false, null: false, after: :suspendable
|
5
|
+
end
|
6
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateExecutionHistories < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :execution_histories do |t|
|
4
|
+
t.string :hostname, limit: 180
|
5
|
+
t.integer :worker_id, limit: 1
|
6
|
+
t.string :queue, limit: 180, default: "@default", null: false
|
7
|
+
t.integer :job_definition_id, null: false
|
8
|
+
t.integer :job_instance_id, null: false
|
9
|
+
t.text :shell, null: false
|
10
|
+
t.datetime :started_at, null: false
|
11
|
+
t.datetime :finished_at, null: false
|
12
|
+
end
|
13
|
+
|
14
|
+
add_column :executions, :hostname, :string, limit: 180
|
15
|
+
add_column :executions, :worker_id, :integer, limit: 1
|
16
|
+
|
17
|
+
add_index :execution_histories, [:worker_id, :started_at], using: :btree
|
18
|
+
end
|
19
|
+
end
|
@@ -19,7 +19,8 @@ module Kuroko2
|
|
19
19
|
elsif worker_id == (Command::Executor.num_workers - 1)
|
20
20
|
Command::Monitor.new(hostname: @hostname, worker_id: worker_id)
|
21
21
|
else
|
22
|
-
@worker = Worker.where(hostname: @hostname, worker_id: worker_id, queue: @queue).
|
22
|
+
@worker = Worker.where(hostname: @hostname, worker_id: worker_id, queue: @queue).first_or_initialize
|
23
|
+
@worker.update!(suspendable: true)
|
23
24
|
Command::Shell.new(hostname: @hostname, worker_id: worker_id, worker: @worker, queue: @queue)
|
24
25
|
end
|
25
26
|
end
|
@@ -14,6 +14,7 @@ module Kuroko2
|
|
14
14
|
|
15
15
|
def execute
|
16
16
|
@worker.reload
|
17
|
+
return nil if @worker.suspended?
|
17
18
|
unless @worker.execution_id?
|
18
19
|
if (execution = Execution.poll(@queue))
|
19
20
|
do_execute(execution)
|
@@ -32,6 +33,7 @@ module Kuroko2
|
|
32
33
|
def do_execute(execution)
|
33
34
|
begin
|
34
35
|
@worker.update_column(:execution_id, execution.id)
|
36
|
+
execution.update(hostname: @hostname, worker_id: @worker_id)
|
35
37
|
|
36
38
|
invoke(execution)
|
37
39
|
rescue SystemCallError => e
|
@@ -13,7 +13,7 @@ module Kuroko2
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def finished_text
|
16
|
-
"Finished
|
16
|
+
"Finished executing '#{@definition.name}'"
|
17
17
|
end
|
18
18
|
|
19
19
|
def launched_text
|
@@ -21,7 +21,7 @@ module Kuroko2
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def back_to_normal_text
|
24
|
-
"
|
24
|
+
"'#{@definition.name}' is back to normal"
|
25
25
|
end
|
26
26
|
|
27
27
|
def retrying_text
|
@@ -33,7 +33,7 @@ module Kuroko2
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def long_elapsed_time_text
|
36
|
-
"The running time
|
36
|
+
"The running time of '#{@definition.name}' is longer than expected."
|
37
37
|
end
|
38
38
|
|
39
39
|
def additional_text
|
@@ -61,6 +61,17 @@ module Kuroko2
|
|
61
61
|
instance.logs.error(message)
|
62
62
|
end
|
63
63
|
|
64
|
+
Kuroko2::ExecutionHistory.create(
|
65
|
+
hostname: execution.hostname,
|
66
|
+
worker_id: execution.worker_id,
|
67
|
+
queue: execution.queue,
|
68
|
+
job_definition: execution.job_definition,
|
69
|
+
job_instance: execution.job_instance,
|
70
|
+
shell: execution.shell,
|
71
|
+
started_at: execution.started_at,
|
72
|
+
finished_at: execution.finished_at,
|
73
|
+
)
|
74
|
+
|
64
75
|
execution.with_lock do
|
65
76
|
execution.destroy
|
66
77
|
execution.success? ? :next : :failure
|
data/lib/kuroko2/version.rb
CHANGED
data/spec/command/shell_spec.rb
CHANGED
@@ -62,6 +62,16 @@ module Kuroko2::Command
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
context 'when all workers are suspended' do
|
66
|
+
let(:worker) { create(:worker, suspended: true) }
|
67
|
+
|
68
|
+
it 'skips execution' do
|
69
|
+
is_expected.to be_nil
|
70
|
+
execution.reload
|
71
|
+
expect(execution.started_at).to be_nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
65
75
|
describe 'memory expectancy calculation' do
|
66
76
|
let(:worker) { create(:worker) }
|
67
77
|
let(:memory_expectancy) { execution.job_definition.memory_expectancy }
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Kuroko2::ExecutionHistoriesController do
|
4
|
+
routes { Kuroko2::Engine.routes }
|
5
|
+
|
6
|
+
before { sign_in }
|
7
|
+
|
8
|
+
let!(:histories) { create_list(:execution_history, 3) }
|
9
|
+
|
10
|
+
describe '#index' do
|
11
|
+
subject! { get :index }
|
12
|
+
|
13
|
+
it do
|
14
|
+
expect(response).to have_http_status(:ok)
|
15
|
+
expect(response).to render_template('index')
|
16
|
+
|
17
|
+
expect(assigns(:histories)).to match_array histories
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with valid queue' do
|
21
|
+
subject! { get :index, params: { queue: '@default' } }
|
22
|
+
|
23
|
+
it do
|
24
|
+
expect(assigns(:histories)).to match_array histories
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with unknown queue' do
|
29
|
+
subject! { get :index, params: { queue: 'unknown' } }
|
30
|
+
|
31
|
+
it do
|
32
|
+
expect(assigns(:histories)).to be_empty
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with valid hostname' do
|
37
|
+
subject! { get :index, params: { hostname: 'rspec' } }
|
38
|
+
|
39
|
+
it do
|
40
|
+
expect(assigns(:histories)).to match_array histories
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with unknown hostname' do
|
45
|
+
subject! { get :index, params: { hostname: 'unknown' } }
|
46
|
+
|
47
|
+
it do
|
48
|
+
expect(assigns(:histories)).to be_empty
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#timeline' do
|
54
|
+
subject! { get :timeline }
|
55
|
+
|
56
|
+
it do
|
57
|
+
expect(response).to have_http_status(:ok)
|
58
|
+
expect(response).to render_template('timeline')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#dataset' do
|
63
|
+
subject! { get :dataset, xhr: true }
|
64
|
+
|
65
|
+
it do
|
66
|
+
expect(response).to have_http_status(:ok)
|
67
|
+
|
68
|
+
expect(assigns(:histories)).to match_array histories
|
69
|
+
expect(assigns(:end_at)).not_to be_nil
|
70
|
+
expect(assigns(:start_at)).to eq(assigns(:end_at) - 1.hour)
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with valid queue' do
|
74
|
+
subject! { get :dataset, xhr: true, params: { queue: '@default' } }
|
75
|
+
|
76
|
+
it do
|
77
|
+
expect(assigns(:histories)).to match_array histories
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with unknown queue' do
|
82
|
+
subject! { get :dataset, xhr: true, params: { queue: 'unknown' } }
|
83
|
+
|
84
|
+
it do
|
85
|
+
expect(assigns(:histories)).to be_empty
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with valid hostname' do
|
90
|
+
subject! { get :dataset, xhr: true, params: { hostname: 'rspec' } }
|
91
|
+
|
92
|
+
it do
|
93
|
+
expect(assigns(:histories)).to match_array histories
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with unknown hostname' do
|
98
|
+
subject! { get :dataset, xhr: true, params: { hostname: 'unknown' } }
|
99
|
+
|
100
|
+
it do
|
101
|
+
expect(assigns(:histories)).to be_empty
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'with period' do
|
106
|
+
subject! { get :dataset, xhr: true, params: { period: period }}
|
107
|
+
|
108
|
+
context '30 minutes' do
|
109
|
+
let(:period) { '30m' }
|
110
|
+
it do
|
111
|
+
expect(assigns(:histories)).to match_array histories
|
112
|
+
expect(assigns(:start_at)).to eq(assigns(:end_at) - 30.minutes)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
context '1 hour' do
|
116
|
+
let(:period) { '1h' }
|
117
|
+
it do
|
118
|
+
expect(assigns(:histories)).to match_array histories
|
119
|
+
expect(assigns(:start_at)).to eq(assigns(:end_at) - 1.hour)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context '1 day' do
|
124
|
+
let(:period) { '1d' }
|
125
|
+
it do
|
126
|
+
expect(assigns(:histories)).to match_array histories
|
127
|
+
expect(assigns(:start_at)).to eq(assigns(:end_at) - 1.day)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context '1 week' do
|
132
|
+
let(:period) { '1w' }
|
133
|
+
it do
|
134
|
+
expect(assigns(:histories)).to match_array histories
|
135
|
+
expect(assigns(:start_at)).to eq(assigns(:end_at) - 1.week)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'with end_at' do
|
141
|
+
let(:end_at) { Time.current + 5.minute }
|
142
|
+
subject! { get :dataset, xhr: true, params: { end_at: end_at } }
|
143
|
+
|
144
|
+
it do
|
145
|
+
expect(assigns(:histories)).to match_array histories
|
146
|
+
expect(assigns(:end_at).strftime("%d-%m-%Y %H:%M:%S")).to eq end_at.strftime("%d-%m-%Y %H:%M:%S")
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'with invalid' do
|
150
|
+
let(:end_at) { 'invalid' }
|
151
|
+
|
152
|
+
it do
|
153
|
+
expect(assigns(:histories)).to match_array histories
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'with start_at' do
|
159
|
+
let(:start_at) { 1.hour.ago(Time.current) }
|
160
|
+
subject! { get :dataset, xhr: true, params: { start_at: start_at } }
|
161
|
+
|
162
|
+
it do
|
163
|
+
expect(assigns(:histories)).to match_array histories
|
164
|
+
expect(assigns(:start_at).strftime("%d-%m-%Y %H:%M:%S")).to eq start_at.strftime("%d-%m-%Y %H:%M:%S")
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'with invalid' do
|
168
|
+
let(:start_at) { 'invalid' }
|
169
|
+
|
170
|
+
it do
|
171
|
+
expect(assigns(:histories)).to match_array histories
|
172
|
+
expect(assigns(:start_at)).to eq(assigns(:end_at) - 1.hour)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -2,4 +2,18 @@ require 'rails_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Kuroko2::WorkersController, :type => :controller do
|
4
4
|
routes { Kuroko2::Engine.routes }
|
5
|
+
|
6
|
+
before { sign_in }
|
7
|
+
|
8
|
+
let(:worker) { create(:worker) }
|
9
|
+
|
10
|
+
describe '#update' do
|
11
|
+
subject! { patch :update, params: { id: worker.id, suspended: true } }
|
12
|
+
|
13
|
+
it do
|
14
|
+
expect(response).to redirect_to(workers_path)
|
15
|
+
|
16
|
+
expect(assigns(:worker).suspended).to be_truthy
|
17
|
+
end
|
18
|
+
end
|
5
19
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 31) do
|
14
14
|
|
15
15
|
create_table "admin_assignments", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
|
16
16
|
t.integer "user_id", null: false
|
@@ -20,6 +20,18 @@ ActiveRecord::Schema.define(version: 30) do
|
|
20
20
|
t.index ["user_id", "job_definition_id"], name: "user_id", unique: true
|
21
21
|
end
|
22
22
|
|
23
|
+
create_table "execution_histories", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
|
24
|
+
t.string "hostname", limit: 180
|
25
|
+
t.integer "worker_id", limit: 1
|
26
|
+
t.string "queue", limit: 180, default: "@default", null: false
|
27
|
+
t.integer "job_definition_id", null: false
|
28
|
+
t.integer "job_instance_id", null: false
|
29
|
+
t.text "shell", null: false
|
30
|
+
t.datetime "started_at", null: false
|
31
|
+
t.datetime "finished_at", null: false
|
32
|
+
t.index ["worker_id", "started_at"], name: "index_kuroko2_execution_histories_on_worker_id_and_started_at"
|
33
|
+
end
|
34
|
+
|
23
35
|
create_table "executions", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
|
24
36
|
t.string "uuid", limit: 36, null: false
|
25
37
|
t.integer "job_definition_id"
|
@@ -38,6 +50,8 @@ ActiveRecord::Schema.define(version: 30) do
|
|
38
50
|
t.datetime "mailed_at"
|
39
51
|
t.datetime "created_at"
|
40
52
|
t.datetime "updated_at"
|
53
|
+
t.string "hostname", limit: 180
|
54
|
+
t.integer "worker_id", limit: 1
|
41
55
|
t.index ["job_definition_id", "token_id"], name: "index_kuroko2_executions_on_job_definition_id_and_token_id", unique: true
|
42
56
|
t.index ["started_at"], name: "started_at"
|
43
57
|
end
|
@@ -196,6 +210,8 @@ ActiveRecord::Schema.define(version: 30) do
|
|
196
210
|
t.string "queue", limit: 180, default: "@default", null: false
|
197
211
|
t.boolean "working", default: false, null: false
|
198
212
|
t.integer "execution_id"
|
213
|
+
t.boolean "suspendable", default: false, null: false
|
214
|
+
t.boolean "suspended", default: false, null: false
|
199
215
|
t.datetime "created_at"
|
200
216
|
t.datetime "updated_at"
|
201
217
|
t.index ["hostname", "worker_id"], name: "hostname", unique: true
|
@@ -0,0 +1,15 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :execution_history, class: Kuroko2::ExecutionHistory do
|
3
|
+
hostname 'rspec'
|
4
|
+
worker_id 1
|
5
|
+
queue '@default'
|
6
|
+
|
7
|
+
job_definition { create(:job_definition) }
|
8
|
+
job_instance { create(:job_instance, job_definition: job_definition) }
|
9
|
+
|
10
|
+
shell 'echo $NAME'
|
11
|
+
|
12
|
+
started_at { Time.current }
|
13
|
+
finished_at { Time.current }
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe 'Shows execution histories', type: :feature do
|
4
|
+
before { sign_in }
|
5
|
+
|
6
|
+
let!(:worker) { create(:worker, hostname: 'rspec') }
|
7
|
+
let(:job_definition) { create(:job_definition) }
|
8
|
+
let!(:histories) { create_list(:execution_history, 3, job_definition: job_definition) }
|
9
|
+
|
10
|
+
it 'shows list of execution histories' do
|
11
|
+
visit kuroko2.workers_path
|
12
|
+
expect(page).to have_content('Kuroko Workers')
|
13
|
+
expect(page).to have_content('rspec')
|
14
|
+
expect(page).to have_content('@default')
|
15
|
+
|
16
|
+
click_on 'rspec'
|
17
|
+
|
18
|
+
expect(page).to have_content('Execution Histories')
|
19
|
+
expect(page).to have_selector('#execution-histories table tbody tr', count: 3)
|
20
|
+
expect(page).to have_content('rspec')
|
21
|
+
expect(page).to have_content('@default')
|
22
|
+
expect(page).to have_content(job_definition.name)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'shows timeline of execution histories' do
|
26
|
+
visit kuroko2.execution_histories_path(hostname: 'rspec')
|
27
|
+
|
28
|
+
expect(page).to have_content('Execution Histories')
|
29
|
+
expect(page).to have_content('Show Timeline')
|
30
|
+
|
31
|
+
click_on 'Show Timeline'
|
32
|
+
|
33
|
+
expect(page).to have_content('Execution Timeline')
|
34
|
+
expect(page).to have_content(job_definition.name)
|
35
|
+
end
|
36
|
+
end
|
@@ -44,4 +44,37 @@ RSpec.describe "Show list of workers", type: :feature do
|
|
44
44
|
expect(page).not_to have_content('echo Hello!')
|
45
45
|
expect(page).to have_selector('#workers table tbody tr td .btn', text: 'Details', count: 0)
|
46
46
|
end
|
47
|
+
|
48
|
+
context 'toggle suspended' do
|
49
|
+
it js: true do
|
50
|
+
visit kuroko2.workers_path
|
51
|
+
expect(page).to have_selector('#workers table tbody tr', count: 2)
|
52
|
+
expect(page).not_to have_content('echo Hello!')
|
53
|
+
expect(page).to have_title('Kuroko Workers « Kuroko 2')
|
54
|
+
expect(page).to have_selector('i.fa.fa-rocket', text: '')
|
55
|
+
have_selector('h1', text: /Kuroko Workers/)
|
56
|
+
|
57
|
+
expect(page).to have_selector('#workers table tbody tr', count: 2)
|
58
|
+
expect(page).to have_selector('#workers table tbody tr td .btn[value=Suspend]', count: 1)
|
59
|
+
expect(page).to have_selector('#workers table tbody tr td .btn[value=Unsuspend]', count: 0)
|
60
|
+
expect(page).to have_selector('#workers table tbody tr td .label', text: 'WORKING', count: 1)
|
61
|
+
expect(page).to have_selector('#workers table tbody tr td .label', text: 'SUSPENDED', count: 0)
|
62
|
+
|
63
|
+
click_on 'Suspend'
|
64
|
+
|
65
|
+
expect(page).to have_selector('#workers table tbody tr', count: 2)
|
66
|
+
expect(page).to have_selector('#workers table tbody tr td .btn[value=Suspend]', count: 0)
|
67
|
+
expect(page).to have_selector('#workers table tbody tr td .btn[value=Unsuspend]', count: 1)
|
68
|
+
expect(page).to have_selector('#workers table tbody tr td .label', text: 'WORKING', count: 0)
|
69
|
+
expect(page).to have_selector('#workers table tbody tr td .label', text: 'SUSPENDED', count: 1)
|
70
|
+
|
71
|
+
click_on 'Unsuspend'
|
72
|
+
|
73
|
+
expect(page).to have_selector('#workers table tbody tr', count: 2)
|
74
|
+
expect(page).to have_selector('#workers table tbody tr td .btn[value=Suspend]', count: 1)
|
75
|
+
expect(page).to have_selector('#workers table tbody tr td .btn[value=Unsuspend]', count: 0)
|
76
|
+
expect(page).to have_selector('#workers table tbody tr td .label', text: 'WORKING', count: 1)
|
77
|
+
expect(page).to have_selector('#workers table tbody tr td .label', text: 'SUSPENDED', count: 0)
|
78
|
+
end
|
79
|
+
end
|
47
80
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kuroko2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Naoto Takai
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-03-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -566,6 +566,7 @@ files:
|
|
566
566
|
- app/controllers/kuroko2/api/stats_controller.rb
|
567
567
|
- app/controllers/kuroko2/application_controller.rb
|
568
568
|
- app/controllers/kuroko2/dashboard_controller.rb
|
569
|
+
- app/controllers/kuroko2/execution_histories_controller.rb
|
569
570
|
- app/controllers/kuroko2/execution_logs_controller.rb
|
570
571
|
- app/controllers/kuroko2/executions_controller.rb
|
571
572
|
- app/controllers/kuroko2/job_definition_stats_controller.rb
|
@@ -606,6 +607,7 @@ files:
|
|
606
607
|
- app/models/kuroko2/api/job_instance_resource.rb
|
607
608
|
- app/models/kuroko2/application_record.rb
|
608
609
|
- app/models/kuroko2/execution.rb
|
610
|
+
- app/models/kuroko2/execution_history.rb
|
609
611
|
- app/models/kuroko2/job_definition.rb
|
610
612
|
- app/models/kuroko2/job_definition_tag.rb
|
611
613
|
- app/models/kuroko2/job_instance.rb
|
@@ -625,6 +627,9 @@ files:
|
|
625
627
|
- app/views/kaminari/list/_paginator.html.slim
|
626
628
|
- app/views/kuroko2/dashboard/_taglist.html.slim
|
627
629
|
- app/views/kuroko2/dashboard/index.html.slim
|
630
|
+
- app/views/kuroko2/execution_histories/dataset.json.jbuilder
|
631
|
+
- app/views/kuroko2/execution_histories/index.html.slim
|
632
|
+
- app/views/kuroko2/execution_histories/timeline.html.slim
|
628
633
|
- app/views/kuroko2/execution_logs/index.json.jbuilder
|
629
634
|
- app/views/kuroko2/executions/index.html.slim
|
630
635
|
- app/views/kuroko2/job_definition_stats/execution_time.json.jbuilder
|
@@ -667,6 +672,7 @@ files:
|
|
667
672
|
- app/views/layouts/kuroko2/application.html.slim
|
668
673
|
- app/views/layouts/mailer.html.erb
|
669
674
|
- app/views/layouts/mailer.text.erb
|
675
|
+
- bin/cleanup_old_execution_histories.rb
|
670
676
|
- bin/cleanup_old_instances.rb
|
671
677
|
- bin/remind_failure.rb
|
672
678
|
- config/initializers/000_kuroko2.rb
|
@@ -697,6 +703,8 @@ files:
|
|
697
703
|
- db/migrate/028_change_exit_status_to_unsigned_tinyint.rb
|
698
704
|
- db/migrate/029_add_execution_id_to_process_signals.rb
|
699
705
|
- db/migrate/030_add_notify_back_to_normal.rb
|
706
|
+
- db/migrate/031_add_suspended_to_workers.rb
|
707
|
+
- db/migrate/031_create_execution_histories.rb
|
700
708
|
- lib/autoload/kuroko2/command/executor.rb
|
701
709
|
- lib/autoload/kuroko2/command/kill.rb
|
702
710
|
- lib/autoload/kuroko2/command/monitor.rb
|
@@ -756,6 +764,7 @@ files:
|
|
756
764
|
- spec/command/monitor_spec.rb
|
757
765
|
- spec/command/shell_spec.rb
|
758
766
|
- spec/controllers/dashboard_controller_spec.rb
|
767
|
+
- spec/controllers/execution_histories_controller_spec.rb
|
759
768
|
- spec/controllers/executions_controller_spec.rb
|
760
769
|
- spec/controllers/job_definition_stats_controller_spec.rb
|
761
770
|
- spec/controllers/job_definitions_controller_spec.rb
|
@@ -1737,6 +1746,7 @@ files:
|
|
1737
1746
|
- spec/dummy/tmp/capybara/capybara-201707041237543496360175.png
|
1738
1747
|
- spec/execution_logger/cloud_watch_logs_spec.rb
|
1739
1748
|
- spec/factories/execution_factory.rb
|
1749
|
+
- spec/factories/execution_history_factory.rb
|
1740
1750
|
- spec/factories/job_definition_factory.rb
|
1741
1751
|
- spec/factories/job_instance_factory.rb
|
1742
1752
|
- spec/factories/job_schedule_factory.rb
|
@@ -1749,6 +1759,7 @@ files:
|
|
1749
1759
|
- spec/factories/user_factory.rb
|
1750
1760
|
- spec/factories/worker_factory.rb
|
1751
1761
|
- spec/features/dashborad_spec.rb
|
1762
|
+
- spec/features/execution_histories_spec.rb
|
1752
1763
|
- spec/features/job_definition_spec.rb
|
1753
1764
|
- spec/features/job_instance_spec.rb
|
1754
1765
|
- spec/features/sign_in_and_out_spec.rb
|
@@ -1764,6 +1775,7 @@ files:
|
|
1764
1775
|
- spec/mailers/notifications_spec.rb
|
1765
1776
|
- spec/memory_sampler_spec.rb
|
1766
1777
|
- spec/models/admin_assignment_spec.rb
|
1778
|
+
- spec/models/execution_history_spec.rb
|
1767
1779
|
- spec/models/execution_spec.rb
|
1768
1780
|
- spec/models/job_definition_spec.rb
|
1769
1781
|
- spec/models/job_instance_spec.rb
|
@@ -1836,6 +1848,7 @@ test_files:
|
|
1836
1848
|
- spec/command/monitor_spec.rb
|
1837
1849
|
- spec/command/shell_spec.rb
|
1838
1850
|
- spec/controllers/dashboard_controller_spec.rb
|
1851
|
+
- spec/controllers/execution_histories_controller_spec.rb
|
1839
1852
|
- spec/controllers/executions_controller_spec.rb
|
1840
1853
|
- spec/controllers/job_definition_stats_controller_spec.rb
|
1841
1854
|
- spec/controllers/job_definitions_controller_spec.rb
|
@@ -2817,6 +2830,7 @@ test_files:
|
|
2817
2830
|
- spec/dummy/tmp/capybara/capybara-201707041237543496360175.png
|
2818
2831
|
- spec/execution_logger/cloud_watch_logs_spec.rb
|
2819
2832
|
- spec/factories/execution_factory.rb
|
2833
|
+
- spec/factories/execution_history_factory.rb
|
2820
2834
|
- spec/factories/job_definition_factory.rb
|
2821
2835
|
- spec/factories/job_instance_factory.rb
|
2822
2836
|
- spec/factories/job_schedule_factory.rb
|
@@ -2829,6 +2843,7 @@ test_files:
|
|
2829
2843
|
- spec/factories/user_factory.rb
|
2830
2844
|
- spec/factories/worker_factory.rb
|
2831
2845
|
- spec/features/dashborad_spec.rb
|
2846
|
+
- spec/features/execution_histories_spec.rb
|
2832
2847
|
- spec/features/job_definition_spec.rb
|
2833
2848
|
- spec/features/job_instance_spec.rb
|
2834
2849
|
- spec/features/sign_in_and_out_spec.rb
|
@@ -2844,6 +2859,7 @@ test_files:
|
|
2844
2859
|
- spec/mailers/notifications_spec.rb
|
2845
2860
|
- spec/memory_sampler_spec.rb
|
2846
2861
|
- spec/models/admin_assignment_spec.rb
|
2862
|
+
- spec/models/execution_history_spec.rb
|
2847
2863
|
- spec/models/execution_spec.rb
|
2848
2864
|
- spec/models/job_definition_spec.rb
|
2849
2865
|
- spec/models/job_instance_spec.rb
|