Commit 7af4f521 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Show warning if build doesn't have runners with specified tags or runners didn't connect recently

Slightly refactor runner status detection: moving it to Runner class
Signed-off-by: default avatarKamil Trzcinski <ayufan@ayufan.eu>
parent 2d0fcb4d
...@@ -27,6 +27,7 @@ v 8.1.0 (unreleased) ...@@ -27,6 +27,7 @@ v 8.1.0 (unreleased)
- Move CI triggers page to project settings area - Move CI triggers page to project settings area
- Move CI project settings page to CE project settings area - Move CI project settings page to CE project settings area
- Fix bug when removed file was not appearing in merge request diff - Fix bug when removed file was not appearing in merge request diff
- Show warning when build cannot be served by any of the available CI runners
- Note the original location of a moved project when notifying users of the move - Note the original location of a moved project when notifying users of the move
- Improve error message when merging fails - Improve error message when merging fails
- Add support of multibyte characters in LDAP UID (Roman Petrov) - Add support of multibyte characters in LDAP UID (Roman Petrov)
......
...@@ -6,7 +6,7 @@ module Ci ...@@ -6,7 +6,7 @@ module Ci
@runners = Ci::Runner.order('id DESC') @runners = Ci::Runner.order('id DESC')
@runners = @runners.search(params[:search]) if params[:search].present? @runners = @runners.search(params[:search]) if params[:search].present?
@runners = @runners.page(params[:page]).per(30) @runners = @runners.page(params[:page]).per(30)
@active_runners_cnt = Ci::Runner.where("contacted_at > ?", 1.minutes.ago).count @active_runners_cnt = Ci::Runner.online.count
end end
def show def show
...@@ -66,7 +66,7 @@ module Ci ...@@ -66,7 +66,7 @@ module Ci
end end
def runner_params def runner_params
params.require(:runner).permit(:token, :description, :tag_list, :contacted_at, :active) params.require(:runner).permit(:token, :description, :tag_list, :active)
end end
end end
end end
...@@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController ...@@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController
end end
def runner_params def runner_params
params.require(:runner).permit(:description, :tag_list, :contacted_at, :active) params.require(:runner).permit(:description, :tag_list, :active)
end end
end end
module RunnersHelper module RunnersHelper
def runner_status_icon(runner) def runner_status_icon(runner)
unless runner.contacted_at status = runner.status
return content_tag :i, nil, case status
class: "fa fa-warning-sign", when :not_connected
title: "New runner. Has not connected yet" content_tag :i, nil,
end class: "fa fa-warning-sign",
title: "New runner. Has not connected yet"
status =
if runner.active?
runner.contacted_at > 3.hour.ago ? :online : :offline
else
:paused
end
content_tag :i, nil, when :online, :offline, :paused
class: "fa fa-circle runner-status-#{status}", content_tag :i, nil,
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" class: "fa fa-circle runner-status-#{status}",
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
end
end end
end end
...@@ -231,6 +231,18 @@ module Ci ...@@ -231,6 +231,18 @@ module Ci
end end
end end
def can_be_served?(runner)
(tag_list - runner.tag_list).empty?
end
def any_runners_online?
project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
end
def show_warning?
pending? && !any_runners_online?
end
private private
def yaml_variables def yaml_variables
......
...@@ -115,12 +115,12 @@ module Ci ...@@ -115,12 +115,12 @@ module Ci
web_url web_url
end end
def any_runners? def any_runners?(&block)
if runners.active.any? if runners.active.any?(&block)
return true return true
end end
shared_runners_enabled && Ci::Runner.shared.active.any? shared_runners_enabled && Ci::Runner.shared.active.any?(&block)
end end
def set_default_values def set_default_values
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
module Ci module Ci
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
extend Ci::Model extend Ci::Model
LAST_CONTACT_TIME = 5.minutes.ago
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
...@@ -33,6 +35,7 @@ module Ci ...@@ -33,6 +35,7 @@ module Ci
scope :shared, ->() { where(is_shared: true) } scope :shared, ->() { where(is_shared: true) }
scope :active, ->() { where(active: true) } scope :active, ->() { where(active: true) }
scope :paused, ->() { where(active: false) } scope :paused, ->() { where(active: false) }
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
acts_as_taggable acts_as_taggable
...@@ -65,6 +68,20 @@ module Ci ...@@ -65,6 +68,20 @@ module Ci
is_shared is_shared
end end
def online?
contacted_at && contacted_at > LAST_CONTACT_TIME
end
def status
if contacted_at.nil?
:not_connected
elsif active?
online? ? :online : :offline
else
:paused
end
end
def belongs_to_one_project? def belongs_to_one_project?
runner_projects.count == 1 runner_projects.count == 1
end end
......
...@@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base ...@@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base
def retry_url def retry_url
nil nil
end end
def show_warning?
false
end
end end
...@@ -17,7 +17,7 @@ module Ci ...@@ -17,7 +17,7 @@ module Ci
builds = builds.order('created_at ASC') builds = builds.order('created_at ASC')
build = builds.find do |build| build = builds.find do |build|
(build.tag_list - current_runner.tag_list).empty? build.can_be_served?(current_runner)
end end
......
...@@ -39,6 +39,27 @@ ...@@ -39,6 +39,27 @@
.pull-right .pull-right
= @build.updated_at.stamp('19:00 Aug 27') = @build.updated_at.stamp('19:00 Aug 27')
- if @build.show_warning?
- unless @build.any_runners_online?
.bs-callout.bs-callout-warning
%p
- if no_runners_for_project?(@build.project)
This build is stuck, because the project doesn't have runners assigned.
- elsif @build.tags.any?
This build is stuck.
%br
This build is stuck, because you don't have any active runners online with these tags assigned to the project:
- @build.tags.each do |tag|
%span.label.label-primary
= tag
- else
This build is stuck, because you don't have any active runners online that can run this build.
%br
Go to
= link_to namespace_project_runners_path(@build.gl_project.namespace, @build.gl_project) do
Runners page
.row.prepend-top-default .row.prepend-top-default
.col-md-9 .col-md-9
.clearfix .clearfix
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
%td.status %td.status
= ci_status_with_icon(commit_status.status) = ci_status_with_icon(commit_status.status)
- if commit_status.show_warning?
.pull-right
%i.fa.fa-warning-sign.text-warning
%td.commit_status-link %td.commit_status-link
- if commit_status.target_url - if commit_status.target_url
= link_to commit_status.target_url do = link_to commit_status.target_url do
......
...@@ -273,4 +273,105 @@ describe Ci::Build do ...@@ -273,4 +273,105 @@ describe Ci::Build do
is_expected.to eq(['rec1', pusher_email]) is_expected.to eq(['rec1', pusher_email])
end end
end end
describe :can_be_served? do
let(:runner) { FactoryGirl.create :ci_specific_runner }
before { build.project.runners << runner }
context 'runner without tags' do
it 'can handle builds without tags' do
expect(build.can_be_served?(runner)).to be_truthy
end
it 'cannot handle build with tags' do
build.tag_list = ['aa']
expect(build.can_be_served?(runner)).to be_falsey
end
end
context 'runner with tags' do
before { runner.tag_list = ['bb', 'cc'] }
it 'can handle builds without tags' do
expect(build.can_be_served?(runner)).to be_truthy
end
it 'can handle build with matching tags' do
build.tag_list = ['bb']
expect(build.can_be_served?(runner)).to be_truthy
end
it 'cannot handle build with not matching tags' do
build.tag_list = ['aa']
expect(build.can_be_served?(runner)).to be_falsey
end
end
end
describe :any_runners_online? do
subject { build.any_runners_online? }
context 'when no runners' do
it { is_expected.to be_falsey }
end
context 'if there are runner' do
let(:runner) { FactoryGirl.create :ci_specific_runner }
before do
build.project.runners << runner
runner.update_attributes(contacted_at: 1.second.ago)
end
it { is_expected.to be_truthy }
it 'that is inactive' do
runner.update_attributes(active: false)
is_expected.to be_falsey
end
it 'that is not online' do
runner.update_attributes(contacted_at: nil)
is_expected.to be_falsey
end
it 'that cannot handle build' do
expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false)
is_expected.to be_falsey
end
end
end
describe :show_warning? do
subject { build.show_warning? }
%w(pending).each do |state|
context "if commit_status.status is #{state}" do
before { build.status = state }
it { is_expected.to be_truthy }
context "and there are specific runner" do
let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago }
before do
build.project.runners << runner
runner.save
end
it { is_expected.to be_falsey }
end
end
end
%w(success failed canceled running).each do |state|
context "if commit_status.status is #{state}" do
before { build.status = state }
it { is_expected.to be_falsey }
end
end
end
end end
...@@ -259,5 +259,18 @@ describe Ci::Project do ...@@ -259,5 +259,18 @@ describe Ci::Project do
FactoryGirl.create(:ci_shared_runner) FactoryGirl.create(:ci_shared_runner)
expect(project.any_runners?).to be_falsey expect(project.any_runners?).to be_falsey
end end
it "checks the presence of specific runner" do
project = FactoryGirl.create(:ci_project)
specific_runner = FactoryGirl.create(:ci_specific_runner)
project.runners << specific_runner
expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
end
it "checks the presence of shared runner" do
project = FactoryGirl.create(:ci_project, shared_runners_enabled: true)
shared_runner = FactoryGirl.create(:ci_shared_runner)
expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
end
end end
end end
...@@ -48,6 +48,71 @@ describe Ci::Runner do ...@@ -48,6 +48,71 @@ describe Ci::Runner do
it { expect(shared_runner.only_for?(project)).to be_truthy } it { expect(shared_runner.only_for?(project)).to be_truthy }
end end
describe :online do
subject { Ci::Runner.online }
before do
@runner1 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.year.ago)
@runner2 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago)
end
it { is_expected.to eq([@runner2])}
end
describe :online? do
let(:runner) { FactoryGirl.create(:ci_shared_runner) }
subject { runner.online? }
context 'never contacted' do
before { runner.contacted_at = nil }
it { is_expected.to be_falsey }
end
context 'contacted long time ago time' do
before { runner.contacted_at = 1.year.ago }
it { is_expected.to be_falsey }
end
context 'contacted 1s ago' do
before { runner.contacted_at = 1.second.ago }
it { is_expected.to be_truthy }
end
end
describe :status do
let(:runner) { FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago) }
subject { runner.status }
context 'never connected' do
before { runner.contacted_at = nil }
it { is_expected.to eq(:not_connected) }
end
context 'contacted 1s ago' do
before { runner.contacted_at = 1.second.ago }
it { is_expected.to eq(:online) }
end
context 'contacted long time ago' do
before { runner.contacted_at = 1.year.ago }
it { is_expected.to eq(:offline) }
end
context 'inactive' do
before { runner.active = false }
it { is_expected.to eq(:paused) }
end
end
describe "belongs_to_one_project?" do describe "belongs_to_one_project?" do
it "returns false if there are two projects runner assigned to" do it "returns false if there are two projects runner assigned to" do
runner = FactoryGirl.create(:ci_specific_runner) runner = FactoryGirl.create(:ci_specific_runner)
......
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