Commit f662bc5a authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'use-update-runner-service' into 'master'

Prefer service object over after_save hook

Closes #26921

See merge request !8664
parents 5cc9ebbe 60288d6c
...@@ -13,7 +13,7 @@ class Admin::RunnersController < Admin::ApplicationController ...@@ -13,7 +13,7 @@ class Admin::RunnersController < Admin::ApplicationController
end end
def update def update
if @runner.update_attributes(runner_params) if Ci::UpdateRunnerService.new(@runner).update(runner_params)
respond_to do |format| respond_to do |format|
format.js format.js
format.html { redirect_to admin_runner_path(@runner) } format.html { redirect_to admin_runner_path(@runner) }
...@@ -31,7 +31,7 @@ class Admin::RunnersController < Admin::ApplicationController ...@@ -31,7 +31,7 @@ class Admin::RunnersController < Admin::ApplicationController
end end
def resume def resume
if @runner.update_attributes(active: true) if Ci::UpdateRunnerService.new(@runner).update(active: true)
redirect_to admin_runners_path, notice: 'Runner was successfully updated.' redirect_to admin_runners_path, notice: 'Runner was successfully updated.'
else else
redirect_to admin_runners_path, alert: 'Runner was not updated.' redirect_to admin_runners_path, alert: 'Runner was not updated.'
...@@ -39,7 +39,7 @@ class Admin::RunnersController < Admin::ApplicationController ...@@ -39,7 +39,7 @@ class Admin::RunnersController < Admin::ApplicationController
end end
def pause def pause
if @runner.update_attributes(active: false) if Ci::UpdateRunnerService.new(@runner).update(active: false)
redirect_to admin_runners_path, notice: 'Runner was successfully updated.' redirect_to admin_runners_path, notice: 'Runner was successfully updated.'
else else
redirect_to admin_runners_path, alert: 'Runner was not updated.' redirect_to admin_runners_path, alert: 'Runner was not updated.'
......
...@@ -12,7 +12,7 @@ class Projects::RunnersController < Projects::ApplicationController ...@@ -12,7 +12,7 @@ class Projects::RunnersController < Projects::ApplicationController
end end
def update def update
if @runner.update_attributes(runner_params) if Ci::UpdateRunnerService.new(@runner).update(runner_params)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else else
render 'edit' render 'edit'
...@@ -28,7 +28,7 @@ class Projects::RunnersController < Projects::ApplicationController ...@@ -28,7 +28,7 @@ class Projects::RunnersController < Projects::ApplicationController
end end
def resume def resume
if @runner.update_attributes(active: true) if Ci::UpdateRunnerService.new(@runner).update(active: true)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else else
redirect_to runner_path(@runner), alert: 'Runner was not updated.' redirect_to runner_path(@runner), alert: 'Runner was not updated.'
...@@ -36,7 +36,7 @@ class Projects::RunnersController < Projects::ApplicationController ...@@ -36,7 +36,7 @@ class Projects::RunnersController < Projects::ApplicationController
end end
def pause def pause
if @runner.update_attributes(active: false) if Ci::UpdateRunnerService.new(@runner).update(active: false)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else else
redirect_to runner_path(@runner), alert: 'Runner was not updated.' redirect_to runner_path(@runner), alert: 'Runner was not updated.'
......
...@@ -22,8 +22,6 @@ module Ci ...@@ -22,8 +22,6 @@ module Ci
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) } scope :ordered, ->() { order(id: :desc) }
after_save :tick_runner_queue, if: :form_editable_changed?
scope :owned_or_shared, ->(project_id) do scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id') joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
...@@ -40,6 +38,8 @@ module Ci ...@@ -40,6 +38,8 @@ module Ci
acts_as_taggable acts_as_taggable
after_destroy :cleanup_runner_queue
# Searches for runners matching the given query. # Searches for runners matching the given query.
# #
# This method uses ILIKE on PostgreSQL and LIKE on MySQL. # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
...@@ -147,14 +147,14 @@ module Ci ...@@ -147,14 +147,14 @@ module Ci
private private
def runner_queue_key def cleanup_runner_queue
"runner:build_queue:#{self.token}" Gitlab::Redis.with do |redis|
redis.del(runner_queue_key)
end
end end
def form_editable_changed? def runner_queue_key
FORM_EDITABLE.any? do |editable| "runner:build_queue:#{self.token}"
public_send("#{editable}_changed?")
end
end end
def tag_constraints def tag_constraints
......
module Ci
class UpdateRunnerService
attr_reader :runner
def initialize(runner)
@runner = runner
end
def update(params)
runner.update(params).tap do |updated|
runner.tick_runner_queue if updated
end
end
end
end
...@@ -60,8 +60,9 @@ module API ...@@ -60,8 +60,9 @@ module API
put ':id' do put ':id' do
runner = get_runner(params.delete(:id)) runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner) authenticate_update_runner!(runner)
update_service = Ci::UpdateRunnerService.new(runner)
if runner.update(declared_params(include_missing: false)) if update_service.update(declared_params(include_missing: false))
present runner, with: Entities::RunnerDetails, current_user: current_user present runner, with: Entities::RunnerDetails, current_user: current_user
else else
render_validation_error!(runner) render_validation_error!(runner)
......
require 'spec_helper'
describe Admin::RunnersController do
let(:runner) { create(:ci_runner) }
before do
sign_in(create(:admin))
end
describe '#index' do
it 'lists all runners' do
get :index
expect(response).to have_http_status(200)
end
end
describe '#show' do
it 'shows a particular runner' do
get :show, id: runner.id
expect(response).to have_http_status(200)
end
it 'shows 404 for unknown runner' do
get :show, id: 0
expect(response).to have_http_status(404)
end
end
describe '#update' do
it 'updates the runner and ticks the queue' do
new_desc = runner.description.swapcase
expect do
post :update, id: runner.id, runner: { description: new_desc }
end.to change { runner.ensure_runner_queue_value }
runner.reload
expect(response).to have_http_status(302)
expect(runner.description).to eq(new_desc)
end
end
describe '#destroy' do
it 'destroys the runner' do
delete :destroy, id: runner.id
expect(response).to have_http_status(302)
expect(Ci::Runner.find_by(id: runner.id)).to be_nil
end
end
describe '#resume' do
it 'marks the runner as active and ticks the queue' do
runner.update(active: false)
expect do
post :resume, id: runner.id
end.to change { runner.ensure_runner_queue_value }
runner.reload
expect(response).to have_http_status(302)
expect(runner.active).to eq(true)
end
end
describe '#pause' do
it 'marks the runner as inactive and ticks the queue' do
runner.update(active: true)
expect do
post :pause, id: runner.id
end.to change { runner.ensure_runner_queue_value }
runner.reload
expect(response).to have_http_status(302)
expect(runner.active).to eq(false)
end
end
end
require 'spec_helper'
describe Projects::RunnersController do
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:runner) { create(:ci_runner) }
let(:params) do
{
namespace_id: project.namespace,
project_id: project,
id: runner
}
end
before do
sign_in(user)
project.add_master(user)
project.runners << runner
end
describe '#update' do
it 'updates the runner and ticks the queue' do
new_desc = runner.description.swapcase
expect do
post :update, params.merge(runner: { description: new_desc } )
end.to change { runner.ensure_runner_queue_value }
runner.reload
expect(response).to have_http_status(302)
expect(runner.description).to eq(new_desc)
end
end
describe '#destroy' do
it 'destroys the runner' do
delete :destroy, params
expect(response).to have_http_status(302)
expect(Ci::Runner.find_by(id: runner.id)).to be_nil
end
end
describe '#resume' do
it 'marks the runner as active and ticks the queue' do
runner.update(active: false)
expect do
post :resume, params
end.to change { runner.ensure_runner_queue_value }
runner.reload
expect(response).to have_http_status(302)
expect(runner.active).to eq(true)
end
end
describe '#pause' do
it 'marks the runner as inactive and ticks the queue' do
runner.update(active: true)
expect do
post :pause, params
end.to change { runner.ensure_runner_queue_value }
runner.reload
expect(response).to have_http_status(302)
expect(runner.active).to eq(false)
end
end
end
...@@ -290,7 +290,7 @@ describe Ci::Runner, models: true do ...@@ -290,7 +290,7 @@ describe Ci::Runner, models: true do
let!(:last_update) { runner.ensure_runner_queue_value } let!(:last_update) { runner.ensure_runner_queue_value }
before do before do
runner.update(description: 'new runner') Ci::UpdateRunnerService.new(runner).update(description: 'new runner')
end end
it 'sets a new last_update value' do it 'sets a new last_update value' do
...@@ -318,6 +318,25 @@ describe Ci::Runner, models: true do ...@@ -318,6 +318,25 @@ describe Ci::Runner, models: true do
end end
end end
describe '#destroy' do
let(:runner) { create(:ci_runner) }
context 'when there is a tick in the queue' do
let!(:queue_key) { runner.send(:runner_queue_key) }
before do
runner.tick_runner_queue
runner.destroy
end
it 'cleans up the queue' do
Gitlab::Redis.with do |redis|
expect(redis.get(queue_key)).to be_nil
end
end
end
end
describe '.assignable_for' do describe '.assignable_for' do
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner) }
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
......
...@@ -183,6 +183,7 @@ describe API::Runners, api: true do ...@@ -183,6 +183,7 @@ describe API::Runners, api: true do
it 'updates runner' do it 'updates runner' do
description = shared_runner.description description = shared_runner.description
active = shared_runner.active active = shared_runner.active
runner_queue_value = shared_runner.ensure_runner_queue_value
update_runner(shared_runner.id, admin, description: "#{description}_updated", update_runner(shared_runner.id, admin, description: "#{description}_updated",
active: !active, active: !active,
...@@ -197,18 +198,24 @@ describe API::Runners, api: true do ...@@ -197,18 +198,24 @@ describe API::Runners, api: true do
expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql') expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
expect(shared_runner.run_untagged?).to be(false) expect(shared_runner.run_untagged?).to be(false)
expect(shared_runner.locked?).to be(true) expect(shared_runner.locked?).to be(true)
expect(shared_runner.ensure_runner_queue_value)
.not_to eq(runner_queue_value)
end end
end end
context 'when runner is not shared' do context 'when runner is not shared' do
it 'updates runner' do it 'updates runner' do
description = specific_runner.description description = specific_runner.description
runner_queue_value = specific_runner.ensure_runner_queue_value
update_runner(specific_runner.id, admin, description: 'test') update_runner(specific_runner.id, admin, description: 'test')
specific_runner.reload specific_runner.reload
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(specific_runner.description).to eq('test') expect(specific_runner.description).to eq('test')
expect(specific_runner.description).not_to eq(description) expect(specific_runner.description).not_to eq(description)
expect(specific_runner.ensure_runner_queue_value)
.not_to eq(runner_queue_value)
end end
end end
......
require 'spec_helper'
describe Ci::UpdateRunnerService, :services do
let(:runner) { create(:ci_runner) }
describe '#update' do
before do
allow(runner).to receive(:tick_runner_queue)
end
context 'with description params' do
let(:params) { { description: 'new runner' } }
it 'updates the runner and ticking the queue' do
expect(update).to be_truthy
runner.reload
expect(runner).to have_received(:tick_runner_queue)
expect(runner.description).to eq('new runner')
end
end
context 'when params are not valid' do
let(:params) { { run_untagged: false } }
it 'does not update and give false because it is not valid' do
expect(update).to be_falsey
runner.reload
expect(runner).not_to have_received(:tick_runner_queue)
expect(runner.run_untagged).to be_truthy
end
end
def update
described_class.new(runner).update(params)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment