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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80baa8f9a1ca15294cc73f9f726f5584433d6acb
4
- data.tar.gz: 57be564404077de0d32b829687cc579ee925a02f
3
+ metadata.gz: d66f084f8f4eabb9f6a0c0b26dad87b4009a4f97
4
+ data.tar.gz: 0256ee9b074d99821adbd5c1a8bf3bb280c29574
5
5
  SHA512:
6
- metadata.gz: 7e657c7824576cecaa2e3cbc2a01abdad723efe66f4dd6da7fa0fe4f68f215f8f46dc3ca3ed7982bb79fb299d736d2363dda21846e604aa61e5c8df03dde934f
7
- data.tar.gz: 296957b071325388c8407d6dabeaabe85b1d6ce1498db6f0d80af9eefb4ed2c161c2e17f5592978057189751d5809b579bcfb00e84ccf040329b20594f7513b3
6
+ metadata.gz: 4412638419a0984cde3aaf825f92b95a4e65eae33f660968635440decb04362b659b06a7a8aa0b18796a3f00dd551fa09a553d92fbe195edcda806f642d1fcf2
7
+ data.tar.gz: 0cb2e9266689d1636c67b5e7eae2ef56556d1b9188b0e2e56abd3c1fff739de7c041feb3ea98a8afe3382f1d0813c8da773773125aed2d244076a693be8d8884
data/README.md CHANGED
@@ -38,6 +38,7 @@ Documentation is available at [docs/index.md](docs/index.md).
38
38
  - t8m8
39
39
  - yohfee
40
40
  - takonomura
41
+ - errm
41
42
 
42
43
  ## License
43
44
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -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,8 @@
1
+ class Kuroko2::ExecutionHistory < Kuroko2::ApplicationRecord
2
+ include Kuroko2::TableNameCustomizable
3
+
4
+ belongs_to :job_definition
5
+ belongs_to :job_instance
6
+
7
+ scope :ordered, -> { order(started_at: :desc) }
8
+ 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-5 Execution
16
+ th.col-md-4 Execution
17
+ th.col-md-1 &nbsp;
17
18
  th.col-md-1 &nbsp;
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.working
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
  '&nbsp;
@@ -39,3 +42,22 @@
39
42
  role: 'button', class: 'btn btn-sm btn-default')
40
43
  - else
41
44
  '&nbsp;
45
+ td
46
+ - if !worker.suspendable
47
+ '&nbsp;
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?' })
@@ -0,0 +1,9 @@
1
+ old_histories = Kuroko2::ExecutionHistory.where('finished_at < ?', 2.weeks.ago)
2
+
3
+ count = old_histories.count
4
+
5
+ Kuroko2::ExecutionHistories.transaction do
6
+ old_histories.destroy_all
7
+ end
8
+
9
+ puts "Destroyed #{count} histories"
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: :index
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).first_or_create!
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 to execute '#{@definition.name}'"
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
- "Backed to normal '#{@definition.name}'"
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 is longer than expected '#{@definition.name}'."
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
@@ -1,3 +1,3 @@
1
1
  module Kuroko2
2
- VERSION = '0.4.4'
2
+ VERSION = '0.4.5'
3
3
  end
@@ -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
@@ -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: 30) do
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
@@ -4,5 +4,6 @@ FactoryGirl.define do
4
4
  sequence(:worker_id)
5
5
  queue "@default"
6
6
  working true
7
+ suspendable true
7
8
  end
8
9
  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
@@ -0,0 +1,4 @@
1
+ require 'rails_helper'
2
+
3
+ describe Kuroko2::ExecutionHistory do
4
+ end
@@ -41,6 +41,7 @@ module Kuroko2::Workflow::Task
41
41
 
42
42
  expect(Kuroko2::Execution.all.size).to eq 0
43
43
  expect(Kuroko2::Log.all.count).to eq 1
44
+ expect(Kuroko2::ExecutionHistory.all.size).to eq 1
44
45
  end
45
46
  end
46
47
 
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
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-02-16 00:00:00.000000000 Z
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