Commit ab489d29 authored by Dylan Griffith's avatar Dylan Griffith

Improve runner_type validations for Ci::Runner

parent ec1d3e10
...@@ -56,7 +56,9 @@ module Ci ...@@ -56,7 +56,9 @@ module Ci
end end
validate :tag_constraints validate :tag_constraints
validate :either_projects_or_group validate :no_projects, unless: :project_type?
validate :no_groups, unless: :group_type?
validate :only_one_group, if: :group_type?
validates :access_level, presence: true validates :access_level, presence: true
validates :runner_type, presence: true validates :runner_type, presence: true
...@@ -253,13 +255,21 @@ module Ci ...@@ -253,13 +255,21 @@ module Ci
self.class.owned_or_shared(project_id).where(id: self.id).any? self.class.owned_or_shared(project_id).where(id: self.id).any?
end end
def either_projects_or_group def no_projects
if groups.many? if projects.any?
errors.add(:runner, 'can only be assigned to one group') errors.add(:runner, 'cannot assign project to a non-project runner')
end
end
def no_groups
if groups.any?
errors.add(:runner, 'cannot assign group to a non-group runner')
end end
end
if assigned_to_group? && assigned_to_project? def only_one_group
errors.add(:runner, 'can only be assigned either to projects or to a group') if groups.many?
errors.add(:runner, 'can only be assigned to one group')
end end
end end
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Groups::RunnersController do describe Groups::RunnersController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner, :group) }
let(:params) do let(:params) do
{ {
......
...@@ -21,6 +21,19 @@ FactoryBot.define do ...@@ -21,6 +21,19 @@ FactoryBot.define do
is_shared false is_shared false
end end
trait :group do
runner_type :group_type
end
trait :project do
runner_type :project_type
end
trait :instance do
is_shared true
runner_type :instance_type
end
trait :inactive do trait :inactive do
active false active false
end end
......
...@@ -62,7 +62,7 @@ describe "Admin Runners" do ...@@ -62,7 +62,7 @@ describe "Admin Runners" do
context 'group runner' do context 'group runner' do
let(:group) { create(:group) } let(:group) { create(:group) }
let!(:runner) { create(:ci_runner, groups: [group], runner_type: :group_type) } let!(:runner) { create(:ci_runner, :group, groups: [group]) }
it 'shows the label and does not show the project count' do it 'shows the label and does not show the project count' do
visit admin_runners_path visit admin_runners_path
......
...@@ -28,8 +28,8 @@ feature 'Runners' do ...@@ -28,8 +28,8 @@ feature 'Runners' do
project.add_master(user) project.add_master(user)
end end
context 'when a specific runner is activated on the project' do context 'when a project_type runner is activated on the project' do
given(:specific_runner) { create(:ci_runner, :specific) } given(:specific_runner) { create(:ci_runner, :project) }
background do background do
project.runners << specific_runner project.runners << specific_runner
...@@ -114,7 +114,7 @@ feature 'Runners' do ...@@ -114,7 +114,7 @@ feature 'Runners' do
end end
context 'when a shared runner is activated on the project' do context 'when a shared runner is activated on the project' do
given!(:shared_runner) { create(:ci_runner, :shared) } given!(:shared_runner) { create(:ci_runner, :instance) }
scenario 'user sees CI/CD setting page' do scenario 'user sees CI/CD setting page' do
visit project_runners_path(project) visit project_runners_path(project)
...@@ -126,7 +126,7 @@ feature 'Runners' do ...@@ -126,7 +126,7 @@ feature 'Runners' do
context 'when a specific runner exists in another project' do context 'when a specific runner exists in another project' do
given(:another_project) { create(:project) } given(:another_project) { create(:project) }
given(:specific_runner) { create(:ci_runner, :specific) } given(:specific_runner) { create(:ci_runner, :project) }
background do background do
another_project.add_master(user) another_project.add_master(user)
...@@ -220,8 +220,8 @@ feature 'Runners' do ...@@ -220,8 +220,8 @@ feature 'Runners' do
end end
context 'project with a group but no group runner' do context 'project with a group but no group runner' do
given(:group) { create :group } given(:group) { create(:group) }
given(:project) { create :project, group: group } given(:project) { create(:project, group: group) }
scenario 'group runners are not available' do scenario 'group runners are not available' do
visit project_runners_path(project) visit project_runners_path(project)
...@@ -234,9 +234,9 @@ feature 'Runners' do ...@@ -234,9 +234,9 @@ feature 'Runners' do
end end
context 'project with a group and a group runner' do context 'project with a group and a group runner' do
given(:group) { create :group } given(:group) { create(:group) }
given(:project) { create :project, group: group } given(:project) { create(:project, group: group) }
given!(:ci_runner) { create :ci_runner, groups: [group], description: 'group-runner' } given!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
scenario 'group runners are available' do scenario 'group runners are available' do
visit project_runners_path(project) visit project_runners_path(project)
...@@ -263,7 +263,7 @@ feature 'Runners' do ...@@ -263,7 +263,7 @@ feature 'Runners' do
end end
context 'group runners in group settings' do context 'group runners in group settings' do
given(:group) { create :group } given(:group) { create(:group) }
background do background do
group.add_master(user) group.add_master(user)
end end
...@@ -277,7 +277,7 @@ feature 'Runners' do ...@@ -277,7 +277,7 @@ feature 'Runners' do
end end
context 'group with a runner' do context 'group with a runner' do
let!(:runner) { create :ci_runner, groups: [group], description: 'group-runner' } let!(:runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
scenario 'the runner is visible' do scenario 'the runner is visible' do
visit group_settings_ci_cd_path(group) visit group_settings_ci_cd_path(group)
......
...@@ -21,60 +21,50 @@ describe Ci::Runner do ...@@ -21,60 +21,50 @@ describe Ci::Runner do
end end
end end
context 'either_projects_or_group' do context '#only_one_group' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:runner) { create(:ci_runner, :group, groups: [group]) }
it 'disallows assigning to a group if already assigned to a group' do it 'disallows assigning group if already assigned to a group' do
runner = create(:ci_runner, groups: [group])
runner.groups << build(:group) runner.groups << build(:group)
expect(runner).not_to be_valid expect(runner).not_to be_valid
expect(runner.errors.full_messages).to eq ['Runner can only be assigned to one group'] expect(runner.errors.full_messages).to include('Runner can only be assigned to one group')
end end
end
it 'disallows assigning to a group if already assigned to a project' do context 'runner_type validations' do
project = create(:project) let(:group) { create(:group) }
runner = create(:ci_runner, projects: [project]) let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let(:project_runner) { create(:ci_runner, :project) }
let(:instance_runner) { create(:ci_runner, :instance) }
runner.groups << build(:group) it 'disallows assigning group to project_type runner' do
project_runner.groups << build(:group)
expect(runner).not_to be_valid expect(project_runner).not_to be_valid
expect(runner.errors.full_messages).to eq ['Runner can only be assigned either to projects or to a group'] expect(project_runner.errors.full_messages).to include('Runner cannot assign group to a non-group runner')
end end
it 'disallows assigning to a project if already assigned to a group' do it 'disallows assigning group to instance_type runner' do
runner = create(:ci_runner, groups: [group]) instance_runner.groups << build(:group)
runner.projects << build(:project)
expect(runner).not_to be_valid expect(instance_runner).not_to be_valid
expect(runner.errors.full_messages).to eq ['Runner can only be assigned either to projects or to a group'] expect(instance_runner.errors.full_messages).to include('Runner cannot assign group to a non-group runner')
end end
it 'allows assigning to a group if not assigned to a group nor a project' do it 'disallows assigning project to group_type runner' do
runner = create(:ci_runner) group_runner.projects << build(:project)
runner.groups << build(:group)
expect(runner).to be_valid expect(group_runner).not_to be_valid
expect(group_runner.errors.full_messages).to include('Runner cannot assign project to a non-project runner')
end end
it 'allows assigning to a project if not assigned to a group nor a project' do it 'disallows assigning project to instance_type runner' do
runner = create(:ci_runner) instance_runner.projects << build(:project)
runner.projects << build(:project)
expect(runner).to be_valid expect(instance_runner).not_to be_valid
end expect(instance_runner.errors.full_messages).to include('Runner cannot assign project to a non-project runner')
it 'allows assigning to a project if already assigned to a project' do
project = create(:project)
runner = create(:ci_runner, projects: [project])
runner.projects << build(:project)
expect(runner).to be_valid
end end
end end
end end
...@@ -110,17 +100,12 @@ describe Ci::Runner do ...@@ -110,17 +100,12 @@ describe Ci::Runner do
describe '.shared' do describe '.shared' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:project) } let(:project) { create(:project) }
let!(:group_runner) { create(:ci_runner, :group) }
let!(:project_runner) { create(:ci_runner, :project) }
let!(:shared_runner) { create(:ci_runner, :instance) }
it 'returns the shared group runner' do it 'returns only shared runners' do
runner = create(:ci_runner, :shared, groups: [group]) expect(described_class.shared).to contain_exactly(shared_runner)
expect(described_class.shared).to eq [runner]
end
it 'returns the shared project runner' do
runner = create(:ci_runner, :shared, projects: [project])
expect(described_class.shared).to eq [runner]
end end
end end
...@@ -141,17 +126,17 @@ describe Ci::Runner do ...@@ -141,17 +126,17 @@ describe Ci::Runner do
describe '.belonging_to_parent_group_of_project' do describe '.belonging_to_parent_group_of_project' do
let(:project) { create(:project, group: group) } let(:project) { create(:project, group: group) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:runner) { create(:ci_runner, :specific, groups: [group]) } let(:runner) { create(:ci_runner, :group, groups: [group]) }
let!(:unrelated_group) { create(:group) } let!(:unrelated_group) { create(:group) }
let!(:unrelated_project) { create(:project, group: unrelated_group) } let!(:unrelated_project) { create(:project, group: unrelated_group) }
let!(:unrelated_runner) { create(:ci_runner, :specific, groups: [unrelated_group]) } let!(:unrelated_runner) { create(:ci_runner, :group, groups: [unrelated_group]) }
it 'returns the specific group runner' do it 'returns the specific group runner' do
expect(described_class.belonging_to_parent_group_of_project(project.id)).to contain_exactly(runner) expect(described_class.belonging_to_parent_group_of_project(project.id)).to contain_exactly(runner)
end end
context 'with a parent group with a runner', :nested_groups do context 'with a parent group with a runner', :nested_groups do
let(:runner) { create(:ci_runner, :specific, groups: [parent_group]) } let(:runner) { create(:ci_runner, :group, groups: [parent_group]) }
let(:project) { create(:project, group: group) } let(:project) { create(:project, group: group) }
let(:group) { create(:group, parent: parent_group) } let(:group) { create(:group, parent: parent_group) }
let(:parent_group) { create(:group) } let(:parent_group) { create(:group) }
...@@ -167,13 +152,13 @@ describe Ci::Runner do ...@@ -167,13 +152,13 @@ describe Ci::Runner do
# group specific # group specific
group = create(:group) group = create(:group)
project = create(:project, group: group) project = create(:project, group: group)
group_runner = create(:ci_runner, :specific, groups: [group]) group_runner = create(:ci_runner, :group, groups: [group])
# project specific # project specific
project_runner = create(:ci_runner, :specific, projects: [project]) project_runner = create(:ci_runner, :project, projects: [project])
# globally shared # globally shared
shared_runner = create(:ci_runner, :shared) shared_runner = create(:ci_runner, :instance)
expect(described_class.owned_or_shared(project.id)).to contain_exactly( expect(described_class.owned_or_shared(project.id)).to contain_exactly(
group_runner, project_runner, shared_runner group_runner, project_runner, shared_runner
...@@ -216,7 +201,7 @@ describe Ci::Runner do ...@@ -216,7 +201,7 @@ describe Ci::Runner do
end end
context 'with group runner' do context 'with group runner' do
let!(:runner) { FactoryBot.create(:ci_runner, runner_type: :group_type) } let!(:runner) { FactoryBot.create(:ci_runner, :group) }
it 'raises an error' do it 'raises an error' do
expect { subject } expect { subject }
...@@ -727,7 +712,7 @@ describe Ci::Runner do ...@@ -727,7 +712,7 @@ describe Ci::Runner do
context 'when group runner' do context 'when group runner' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:runner) { create(:ci_runner, description: 'Group runner', groups: [group]) } let(:runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
...@@ -737,18 +722,18 @@ describe Ci::Runner do ...@@ -737,18 +722,18 @@ describe Ci::Runner do
subject { runner.assigned_to_project? } subject { runner.assigned_to_project? }
context 'when group runner' do context 'when group runner' do
let(:runner) { create(:ci_runner, description: 'Group runner', groups: [group]) } let(:runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
let(:group) { create(:group) } let(:group) { create(:group) }
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end end
context 'when shared runner' do context 'when shared runner' do
let(:runner) { create(:ci_runner, :shared, description: 'Shared runner') } let(:runner) { create(:ci_runner, :instance, description: 'Shared runner') }
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end end
context 'when project runner' do context 'when project runner' do
let(:runner) { create(:ci_runner, description: 'Group runner', projects: [project]) } let(:runner) { create(:ci_runner, description: 'Project runner', projects: [project]) }
let(:project) { create(:project) } let(:project) { create(:project) }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
......
...@@ -1238,7 +1238,7 @@ describe Project do ...@@ -1238,7 +1238,7 @@ describe Project do
context 'group runners' do context 'group runners' do
let(:project) { create(:project, group_runners_enabled: group_runners_enabled) } let(:project) { create(:project, group_runners_enabled: group_runners_enabled) }
let(:group) { create(:group, projects: [project]) } let(:group) { create(:group, projects: [project]) }
let(:group_runner) { create(:ci_runner, groups: [group]) } let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
context 'for group runners disabled' do context 'for group runners disabled' do
let(:group_runners_enabled) { false } let(:group_runners_enabled) { false }
...@@ -1279,7 +1279,7 @@ describe Project do ...@@ -1279,7 +1279,7 @@ describe Project do
end end
describe '#shared_runners' do describe '#shared_runners' do
let!(:runner) { create(:ci_runner, :shared) } let!(:runner) { create(:ci_runner, :instance) }
subject { project.shared_runners } subject { project.shared_runners }
......
...@@ -27,7 +27,7 @@ describe API::Runners do ...@@ -27,7 +27,7 @@ describe API::Runners do
end end
end end
let!(:group_runner) { create(:ci_runner, description: 'Group runner', groups: [group], runner_type: :group_type) } let!(:group_runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
before do before do
# Set project access for users # Set project access for users
......
...@@ -7,7 +7,7 @@ module Ci ...@@ -7,7 +7,7 @@ module Ci
set(:pipeline) { create(:ci_pipeline, project: project) } set(:pipeline) { create(:ci_pipeline, project: project) }
let!(:shared_runner) { create(:ci_runner, is_shared: true) } let!(:shared_runner) { create(:ci_runner, is_shared: true) }
let!(:specific_runner) { create(:ci_runner, is_shared: false) } let!(:specific_runner) { create(:ci_runner, is_shared: false) }
let!(:group_runner) { create(:ci_runner, groups: [group], runner_type: :group_type) } let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let!(:pending_job) { create(:ci_build, pipeline: pipeline) } let!(:pending_job) { create(:ci_build, pipeline: pipeline) }
before do before do
...@@ -181,24 +181,24 @@ module Ci ...@@ -181,24 +181,24 @@ module Ci
end end
context 'for multiple builds' do context 'for multiple builds' do
let!(:project2) { create :project, group_runners_enabled: true, group: group } let!(:project2) { create(:project, group_runners_enabled: true, group: group) }
let!(:pipeline2) { create :ci_pipeline, project: project2 } let!(:pipeline2) { create(:ci_pipeline, project: project2) }
let!(:project3) { create :project, group_runners_enabled: true, group: group } let!(:project3) { create(:project, group_runners_enabled: true, group: group) }
let!(:pipeline3) { create :ci_pipeline, project: project3 } let!(:pipeline3) { create(:ci_pipeline, project: project3) }
let!(:build1_project1) { pending_job } let!(:build1_project1) { pending_job }
let!(:build2_project1) { create :ci_build, pipeline: pipeline } let!(:build2_project1) { create(:ci_build, pipeline: pipeline) }
let!(:build3_project1) { create :ci_build, pipeline: pipeline } let!(:build3_project1) { create(:ci_build, pipeline: pipeline) }
let!(:build1_project2) { create :ci_build, pipeline: pipeline2 } let!(:build1_project2) { create(:ci_build, pipeline: pipeline2) }
let!(:build2_project2) { create :ci_build, pipeline: pipeline2 } let!(:build2_project2) { create(:ci_build, pipeline: pipeline2) }
let!(:build1_project3) { create :ci_build, pipeline: pipeline3 } let!(:build1_project3) { create(:ci_build, pipeline: pipeline3) }
# these shouldn't influence the scheduling # these shouldn't influence the scheduling
let!(:unrelated_group) { create :group } let!(:unrelated_group) { create(:group) }
let!(:unrelated_project) { create :project, group_runners_enabled: true, group: unrelated_group } let!(:unrelated_project) { create(:project, group_runners_enabled: true, group: unrelated_group) }
let!(:unrelated_pipeline) { create :ci_pipeline, project: unrelated_project } let!(:unrelated_pipeline) { create(:ci_pipeline, project: unrelated_project) }
let!(:build1_unrelated_project) { create :ci_build, pipeline: unrelated_pipeline } let!(:build1_unrelated_project) { create(:ci_build, pipeline: unrelated_pipeline) }
let!(:unrelated_group_runner) { create :ci_runner, groups: [unrelated_group] } let!(:unrelated_group_runner) { create(:ci_runner, :group, groups: [unrelated_group]) }
it 'does not consider builds from other group runners' do it 'does not consider builds from other group runners' do
expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 6 expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 6
......
...@@ -6,7 +6,7 @@ describe Ci::UpdateBuildQueueService do ...@@ -6,7 +6,7 @@ describe Ci::UpdateBuildQueueService do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when updating specific runners' do context 'when updating specific runners' do
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner, :project) }
context 'when there is a runner that can pick build' do context 'when there is a runner that can pick build' do
before do before do
...@@ -26,7 +26,7 @@ describe Ci::UpdateBuildQueueService do ...@@ -26,7 +26,7 @@ describe Ci::UpdateBuildQueueService do
end end
context 'when updating shared runners' do context 'when updating shared runners' do
let(:runner) { create(:ci_runner, :shared) } let(:runner) { create(:ci_runner, :instance) }
context 'when there is no runner that can pick build' do context 'when there is no runner that can pick build' do
it 'ticks runner queue value' do it 'ticks runner queue value' do
...@@ -56,9 +56,9 @@ describe Ci::UpdateBuildQueueService do ...@@ -56,9 +56,9 @@ describe Ci::UpdateBuildQueueService do
end end
context 'when updating group runners' do context 'when updating group runners' do
let(:group) { create :group } let(:group) { create(:group) }
let(:project) { create :project, group: group } let(:project) { create(:project, group: group) }
let(:runner) { create :ci_runner, groups: [group] } let(:runner) { create(:ci_runner, :group, groups: [group]) }
context 'when there is a runner that can pick build' do context 'when there is a runner that can pick build' do
it 'ticks runner queue value' do it 'ticks runner queue value' do
......
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