diff --git a/app/controllers/kuroko2/workers_controller.rb b/app/controllers/kuroko2/workers_controller.rb index ab438e70..d9d89e8a 100644 --- a/app/controllers/kuroko2/workers_controller.rb +++ b/app/controllers/kuroko2/workers_controller.rb @@ -1,5 +1,22 @@ class Kuroko2::WorkersController < Kuroko2::ApplicationController + before_action :set_worker, only: %i(update) + def index @workers = Kuroko2::Worker.ordered.all end + + def update + @worker.update_attributes(worker_params) + redirect_to workers_path + end + + private + + def set_worker + @worker = Kuroko2::Worker.find(params[:id]) + end + + def worker_params + params.permit(:suspended) + end end diff --git a/app/views/kuroko2/workers/index.html.slim b/app/views/kuroko2/workers/index.html.slim index a6ae492e..411a929b 100644 --- a/app/views/kuroko2/workers/index.html.slim +++ b/app/views/kuroko2/workers/index.html.slim @@ -13,7 +13,8 @@ th.col-md-1 WID th.col-md-2 Queue th.col-md-1 Status - th.col-md-5 Execution + th.col-md-4 Execution + th.col-md-1   th.col-md-1   - for worker in @workers tr @@ -21,7 +22,9 @@ td= worker.worker_id td= worker.queue td - - if worker.working + - if worker.suspended + span.label.label-warning SUSPENDED + - elsif worker.working span.label.label-primary WORKING - else '  @@ -39,3 +42,22 @@ role: 'button', class: 'btn btn-sm btn-default') - else '  + td + - if !worker.suspendable + '  + - elsif !worker.suspended + = button_to('Suspend', + worker_path(worker), + method: :patch, + params: { suspended: true }, + role: 'button', + class: 'btn btn-xs btn-default', + data: { confirm: 'Continue suspending the worker?' }) + - else + = button_to('Unsuspend', + worker_path(worker), + method: :patch, + params: { suspended: false }, + role: 'button', + class: 'btn btn-xs btn-default', + data: { confirm: 'Continue unsuspending the worker?' }) diff --git a/config/routes.rb b/config/routes.rb index 24ba3ab4..7b6f843c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -22,7 +22,7 @@ get 'page/:page', action: :index, on: :collection, as: 'paged' end - resources :workers, only: :index + resources :workers, only: %i(index update) resources :job_instances, path: 'instances', only: %w() do get :working, action: :working, on: :collection end diff --git a/db/migrate/031_add_suspended_to_workers.rb b/db/migrate/031_add_suspended_to_workers.rb new file mode 100644 index 00000000..e6cee987 --- /dev/null +++ b/db/migrate/031_add_suspended_to_workers.rb @@ -0,0 +1,6 @@ +class AddSuspendedToWorkers < ActiveRecord::Migration[5.0] + def change + add_column :workers, :suspendable, :boolean, default: false, null: false, after: :execution_id + add_column :workers, :suspended, :boolean, default: false, null: false, after: :suspendable + end +end diff --git a/lib/autoload/kuroko2/command/executor.rb b/lib/autoload/kuroko2/command/executor.rb index e4862761..35fc8629 100644 --- a/lib/autoload/kuroko2/command/executor.rb +++ b/lib/autoload/kuroko2/command/executor.rb @@ -19,7 +19,8 @@ def initialize elsif worker_id == (Command::Executor.num_workers - 1) Command::Monitor.new(hostname: @hostname, worker_id: worker_id) else - @worker = Worker.where(hostname: @hostname, worker_id: worker_id, queue: @queue).first_or_create! + @worker = Worker.where(hostname: @hostname, worker_id: worker_id, queue: @queue).first_or_initialize + @worker.update!(suspendable: true) Command::Shell.new(hostname: @hostname, worker_id: worker_id, worker: @worker, queue: @queue) end end diff --git a/lib/autoload/kuroko2/command/shell.rb b/lib/autoload/kuroko2/command/shell.rb index c58ecefe..f0ff2f26 100644 --- a/lib/autoload/kuroko2/command/shell.rb +++ b/lib/autoload/kuroko2/command/shell.rb @@ -14,6 +14,7 @@ def initialize(hostname:, worker_id: 0, worker:, queue: Execution::DEFAULT_QUEUE def execute @worker.reload + return nil if @worker.suspended? unless @worker.execution_id? if (execution = Execution.poll(@queue)) do_execute(execution) diff --git a/spec/command/shell_spec.rb b/spec/command/shell_spec.rb index ca325009..bc80b38a 100644 --- a/spec/command/shell_spec.rb +++ b/spec/command/shell_spec.rb @@ -62,6 +62,16 @@ module Kuroko2::Command end end + context 'when all workers are suspended' do + let(:worker) { create(:worker, suspended: true) } + + it 'skips execution' do + is_expected.to be_nil + execution.reload + expect(execution.started_at).to be_nil + end + end + describe 'memory expectancy calculation' do let(:worker) { create(:worker) } let(:memory_expectancy) { execution.job_definition.memory_expectancy } diff --git a/spec/controllers/workers_controller_spec.rb b/spec/controllers/workers_controller_spec.rb index a7a1258a..5267e562 100644 --- a/spec/controllers/workers_controller_spec.rb +++ b/spec/controllers/workers_controller_spec.rb @@ -2,4 +2,18 @@ RSpec.describe Kuroko2::WorkersController, :type => :controller do routes { Kuroko2::Engine.routes } + + before { sign_in } + + let(:worker) { create(:worker) } + + describe '#update' do + subject! { patch :update, params: { id: worker.id, suspended: true } } + + it do + expect(response).to redirect_to(workers_path) + + expect(assigns(:worker).suspended).to be_truthy + end + end end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb index 61f7f24d..e86f02e0 100644 --- a/spec/dummy/db/schema.rb +++ b/spec/dummy/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 30) do +ActiveRecord::Schema.define(version: 31) do create_table "admin_assignments", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t| t.integer "user_id", null: false @@ -196,6 +196,8 @@ t.string "queue", limit: 180, default: "@default", null: false t.boolean "working", default: false, null: false t.integer "execution_id" + t.boolean "suspendable", default: false, null: false + t.boolean "suspended", default: false, null: false t.datetime "created_at" t.datetime "updated_at" t.index ["hostname", "worker_id"], name: "hostname", unique: true diff --git a/spec/factories/worker_factory.rb b/spec/factories/worker_factory.rb index 60cc112f..37608080 100644 --- a/spec/factories/worker_factory.rb +++ b/spec/factories/worker_factory.rb @@ -4,5 +4,6 @@ sequence(:worker_id) queue "@default" working true + suspendable true end end diff --git a/spec/features/workers_spec.rb b/spec/features/workers_spec.rb index b3161113..b9ed7803 100644 --- a/spec/features/workers_spec.rb +++ b/spec/features/workers_spec.rb @@ -44,4 +44,37 @@ expect(page).not_to have_content('echo Hello!') expect(page).to have_selector('#workers table tbody tr td .btn', text: 'Details', count: 0) end + + context 'toggle suspended' do + it js: true do + visit kuroko2.workers_path + expect(page).to have_selector('#workers table tbody tr', count: 2) + expect(page).not_to have_content('echo Hello!') + expect(page).to have_title('Kuroko Workers « Kuroko 2') + expect(page).to have_selector('i.fa.fa-rocket', text: '') + have_selector('h1', text: /Kuroko Workers/) + + expect(page).to have_selector('#workers table tbody tr', count: 2) + expect(page).to have_selector('#workers table tbody tr td .btn[value=Suspend]', count: 1) + expect(page).to have_selector('#workers table tbody tr td .btn[value=Unsuspend]', count: 0) + expect(page).to have_selector('#workers table tbody tr td .label', text: 'WORKING', count: 1) + expect(page).to have_selector('#workers table tbody tr td .label', text: 'SUSPENDED', count: 0) + + click_on 'Suspend' + + expect(page).to have_selector('#workers table tbody tr', count: 2) + expect(page).to have_selector('#workers table tbody tr td .btn[value=Suspend]', count: 0) + expect(page).to have_selector('#workers table tbody tr td .btn[value=Unsuspend]', count: 1) + expect(page).to have_selector('#workers table tbody tr td .label', text: 'WORKING', count: 0) + expect(page).to have_selector('#workers table tbody tr td .label', text: 'SUSPENDED', count: 1) + + click_on 'Unsuspend' + + expect(page).to have_selector('#workers table tbody tr', count: 2) + expect(page).to have_selector('#workers table tbody tr td .btn[value=Suspend]', count: 1) + expect(page).to have_selector('#workers table tbody tr td .btn[value=Unsuspend]', count: 0) + expect(page).to have_selector('#workers table tbody tr td .label', text: 'WORKING', count: 1) + expect(page).to have_selector('#workers table tbody tr td .label', text: 'SUSPENDED', count: 0) + end + end end