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

Merge branch 'feature/sm/33281-protected-runner-executes-jobs-on-protected-branch' into 'master'

Protected runner executes jobs on protected branch [Solution 1]

Closes #33281

See merge request !13194
parents a343484b 53b5346d
......@@ -3,6 +3,7 @@ module Ci
include TokenAuthenticatable
include AfterCommitQueue
include Presentable
include Importable
belongs_to :runner
belongs_to :trigger_request
......@@ -26,6 +27,7 @@ module Ci
validates :coverage, numericality: true, allow_blank: true
validates :ref, presence: true
validates :protected, inclusion: { in: [true, false], unless: :importing? }, on: :create
scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) }
......@@ -34,6 +36,7 @@ module Ci
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
scope :ref_protected, -> { where(protected: true) }
mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
......
......@@ -36,6 +36,7 @@ module Ci
validates :sha, presence: { unless: :importing? }
validates :ref, presence: { unless: :importing? }
validates :status, presence: { unless: :importing? }
validates :protected, inclusion: { in: [true, false], unless: :importing? }, on: :create
validate :valid_commit_sha, unless: :importing?
after_create :keep_around_commits, unless: :importing?
......
......@@ -5,7 +5,7 @@ module Ci
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour
AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level].freeze
has_many :builds
has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
......@@ -35,11 +35,17 @@ module Ci
end
validate :tag_constraints
validates :access_level, presence: true
acts_as_taggable
after_destroy :cleanup_runner_queue
enum access_level: {
not_protected: 0,
ref_protected: 1
}
# Searches for runners matching the given query.
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
......@@ -106,6 +112,8 @@ module Ci
end
def can_pick?(build)
return false if self.ref_protected? && !build.protected?
assignable_for?(build.project) && accepting_tags?(build)
end
......
......@@ -12,7 +12,8 @@ module Ci
tag: tag?,
trigger_requests: Array(trigger_request),
user: current_user,
pipeline_schedule: schedule
pipeline_schedule: schedule,
protected: project.protected_for?(ref)
)
result = validate(current_user,
......
......@@ -77,7 +77,9 @@ module Ci
end
def new_builds
Ci::Build.pending.unstarted
builds = Ci::Build.pending.unstarted
builds = builds.ref_protected if runner.ref_protected?
builds
end
def shared_runner_build_limits_feature_enabled?
......
......@@ -3,7 +3,7 @@ module Ci
CLONE_ACCESSORS = %i[pipeline project ref tag options commands name
allow_failure stage_id stage stage_idx trigger_request
yaml_variables when environment coverage_regex
description tag_list].freeze
description tag_list protected].freeze
def execute(build)
reprocess!(build).tap do |new_build|
......
......@@ -6,6 +6,12 @@
.checkbox
= f.check_box :active
%span.light Paused Runners don't accept new jobs
.form-group
= label :protected, "Protected", class: 'control-label'
.col-sm-10
.checkbox
= f.check_box :access_level, {}, 'ref_protected', 'not_protected'
%span.light This runner will only run on pipelines trigged on protected branches
.form-group
= label :run_untagged, 'Run untagged jobs', class: 'control-label'
.col-sm-10
......
......@@ -19,6 +19,9 @@
%tr
%td Active
%td= @runner.active? ? 'Yes' : 'No'
%tr
%td Protected
%td= @runner.ref_protected? ? 'Yes' : 'No'
%tr
%td Can run untagged jobs
%td= @runner.run_untagged? ? 'Yes' : 'No'
......
---
title: Protected runners
merge_request: 13194
author:
type: added
class AddAccessLevelToCiRunners < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:ci_runners, :access_level, :integer,
default: Ci::Runner.access_levels['not_protected'])
end
def down
remove_column(:ci_runners, :access_level)
end
end
class AddProtectedToCiBuilds < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :ci_builds, :protected, :boolean
end
end
class AddProtectedToCiPipelines < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :ci_pipelines, :protected, :boolean
end
end
class AddIndexOnCiBuildsProtected < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :ci_builds, :protected
end
def down
remove_concurrent_index :ci_builds, :protected if index_exists?(:ci_builds, :protected)
end
end
......@@ -246,6 +246,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.integer "auto_canceled_by_id"
t.boolean "retried"
t.integer "stage_id"
t.boolean "protected"
end
add_index "ci_builds", ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree
......@@ -254,6 +255,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
......@@ -336,6 +338,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.integer "auto_canceled_by_id"
t.integer "pipeline_schedule_id"
t.integer "source"
t.boolean "protected"
end
add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
......@@ -371,6 +374,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.string "architecture"
t.boolean "run_untagged", default: true, null: false
t.boolean "locked", default: false, null: false
t.integer "access_level", default: 0, null: false
end
add_index "ci_runners", ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree
......
......@@ -138,7 +138,8 @@ Example response:
"ruby",
"mysql"
],
"version": null
"version": null,
"access_level": "ref_protected"
}
```
......@@ -156,6 +157,9 @@ PUT /runners/:id
| `description` | string | no | The description of a runner |
| `active` | boolean | no | The state of a runner; can be set to `true` or `false` |
| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
| `run_untagged` | boolean | no | Flag indicating the runner can execute untagged jobs |
| `locked` | boolean | no | Flag indicating the runner is locked |
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
```
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/6" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
......@@ -190,7 +194,8 @@ Example response:
"tag1",
"tag2"
],
"version": null
"version": null,
"access_level": "ref_protected"
}
```
......
......@@ -107,6 +107,26 @@ To lock/unlock a Runner:
1. Check the **Lock to current projects** option
1. Click **Save changes** for the changes to take effect
## Protected Runners
>**Notes:**
[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13194)
in GitLab 10.0.
You can protect Runners from revealing sensitive information.
Whenever a Runner is protected, the Runner picks only jobs created on
[protected branches] or [protected tags], and ignores other jobs.
To protect/unprotect Runners:
1. Visit your project's **Settings ➔ Pipelines**
1. Find a Runner you want to protect/unprotect and make sure it's enabled
1. Click the pencil button besides the Runner name
1. Check the **Protected** option
1. Click **Save changes** for the changes to take effect
![specific Runners edit icon](img/protected_runners_check_box.png)
## How shared Runners pick jobs
Shared Runners abide to a process queue we call fair usage. The fair usage
......@@ -218,3 +238,5 @@ We're always looking for contributions that can mitigate these
[install]: http://docs.gitlab.com/runner/install/
[fifo]: https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
[register]: http://docs.gitlab.com/runner/register/
[protected branches]: ../../user/project/protected_branches.md
[protected tags]: ../../user/project/protected_tags.md
......@@ -37,7 +37,8 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps
step 'pages are deployed' do
pipeline = @project.pipelines.create(ref: 'HEAD',
sha: @project.commit('HEAD').sha,
source: :push)
source: :push,
protected: false)
build = build(:ci_build,
project: @project,
......
......@@ -74,7 +74,8 @@ module API
source: :external,
sha: commit.sha,
ref: ref,
user: current_user)
user: current_user,
protected: @project.protected_for?(ref))
end
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
......@@ -82,7 +83,8 @@ module API
pipeline: pipeline,
name: name,
ref: ref,
user: current_user
user: current_user,
protected: @project.protected_for?(ref)
)
optional_attributes =
......
......@@ -775,6 +775,7 @@ module API
expose :tag_list
expose :run_untagged
expose :locked
expose :access_level
expose :version, :revision, :platform, :architecture
expose :contacted_at
expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.is_shared? }
......
......@@ -55,7 +55,9 @@ module API
optional :tag_list, type: Array[String], desc: 'The list of tags for a runner'
optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
at_least_one_of :description, :active, :tag_list, :run_untagged, :locked
optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level
end
put ':id' do
runner = get_runner(params.delete(:id))
......
......@@ -28,7 +28,8 @@ module Gitlab
attributes.merge(project: project,
ref: pipeline.ref,
tag: pipeline.tag,
trigger_request: trigger)
trigger_request: trigger,
protected: protected_ref?)
end
end
......@@ -43,6 +44,12 @@ module Gitlab
end
end
end
private
def protected_ref?
@protected_ref ||= project.protected_for?(pipeline.ref)
end
end
end
end
......
......@@ -12,6 +12,7 @@ FactoryGirl.define do
started_at 'Di 29. Okt 09:51:28 CET 2013'
finished_at 'Di 29. Okt 09:53:28 CET 2013'
commands 'ls -a'
protected false
options do
{
......@@ -226,5 +227,9 @@ FactoryGirl.define do
status 'created'
self.when 'manual'
end
trait :protected do
protected true
end
end
end
......@@ -4,6 +4,7 @@ FactoryGirl.define do
ref 'master'
sha '97de212e80737a608d939f648d959671fb0a0142'
status 'pending'
protected false
project
......@@ -59,6 +60,10 @@ FactoryGirl.define do
trait :failed do
status :failed
end
trait :protected do
protected true
end
end
end
end
......@@ -5,6 +5,7 @@ FactoryGirl.define do
platform "darwin"
is_shared false
active true
access_level :not_protected
trait :online do
contacted_at Time.now
......@@ -21,5 +22,9 @@ FactoryGirl.define do
trait :inactive do
active false
end
trait :ref_protected do
access_level :ref_protected
end
end
end
......@@ -43,6 +43,21 @@ feature 'Runners' do
expect(page).not_to have_content(specific_runner.display_name)
end
scenario 'user edits the runner to be protected' do
visit runners_path(project)
within '.activated-specific-runners' do
first('.edit-runner > a').click
end
expect(page.find_field('runner[access_level]')).not_to be_checked
check 'runner_access_level'
click_button 'Save changes'
expect(page).to have_content 'Protected Yes'
end
context 'when a runner has a tag' do
background do
specific_runner.update(tag_list: ['tag'])
......
......@@ -27,6 +27,26 @@ describe Gitlab::Ci::Stage::Seed do
expect(subject.builds)
.to all(include(trigger_request: pipeline.trigger_requests.first))
end
context 'when a ref is protected' do
before do
allow_any_instance_of(Project).to receive(:protected_for?).and_return(true)
end
it 'returns protected builds' do
expect(subject.builds).to all(include(protected: true))
end
end
context 'when a ref is unprotected' do
before do
allow_any_instance_of(Project).to receive(:protected_for?).and_return(false)
end
it 'returns unprotected builds' do
expect(subject.builds).to all(include(protected: false))
end
end
end
describe '#user=' do
......
......@@ -224,6 +224,7 @@ Ci::Pipeline:
- lock_version
- auto_canceled_by_id
- pipeline_schedule_id
- protected
Ci::Stage:
- id
- name
......@@ -276,6 +277,7 @@ CommitStatus:
- coverage_regex
- auto_canceled_by_id
- retried
- protected
Ci::Variable:
- id
- project_id
......
......@@ -43,6 +43,32 @@ describe Ci::Build do
it { is_expected.not_to include(manual_but_created) }
end
describe '.ref_protected' do
subject { described_class.ref_protected }
context 'when protected is true' do
let!(:job) { create(:ci_build, :protected) }
it { is_expected.to include(job) }
end
context 'when protected is false' do
let!(:job) { create(:ci_build) }
it { is_expected.not_to include(job) }
end
context 'when protected is nil' do
let!(:job) { create(:ci_build) }
before do
job.update_attribute(:protected, nil)
end
it { is_expected.not_to include(job) }
end
end
describe '#actionize' do
context 'when build is a created' do
before do
......
......@@ -2,6 +2,8 @@ require 'spec_helper'
describe Ci::Runner do
describe 'validation' do
it { is_expected.to validate_presence_of(:access_level) }
context 'when runner is not allowed to pick untagged jobs' do
context 'when runner does not have tags' do
it 'is not valid' do
......@@ -19,6 +21,34 @@ describe Ci::Runner do
end
end
describe '#access_level' do
context 'when creating new runner and access_level is nil' do
let(:runner) do
build(:ci_runner, access_level: nil)
end
it "object is invalid" do
expect(runner).not_to be_valid
end
end
context 'when creating new runner and access_level is defined in enum' do
let(:runner) do
build(:ci_runner, access_level: :not_protected)
end
it "object is valid" do
expect(runner).to be_valid
end
end
context 'when creating new runner and access_level is not defined in enum' do
it "raises an error" do
expect { build(:ci_runner, access_level: :this_is_not_defined) }.to raise_error(ArgumentError)
end
end
end
describe '#display_name' do
it 'returns the description if it has a value' do
runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
......@@ -95,6 +125,8 @@ describe Ci::Runner do
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:runner) { create(:ci_runner) }
subject { runner.can_pick?(build) }
before do
build.project.runners << runner
end
......@@ -222,6 +254,50 @@ describe Ci::Runner do
end
end
end
context 'when access_level of runner is not_protected' do
before do
runner.not_protected!
end
context 'when build is protected' do
before do
build.protected = true
end
it { is_expected.to be_truthy }
end
context 'when build is unprotected' do
before do
build.protected = false
end
it { is_expected.to be_truthy }
end
end
context 'when access_level of runner is ref_protected' do
before do
runner.ref_protected!
end
context 'when build is protected' do
before do
build.protected = true
end
it { is_expected.to be_truthy }
end
context 'when build is unprotected' do
before do
build.protected = false
end
it { is_expected.to be_falsey }
end
end
end
describe '#status' do
......
......@@ -16,8 +16,8 @@ describe API::CommitStatuses do
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
context 'ci commit exists' do
let!(:master) { project.pipelines.create(source: :push, sha: commit.id, ref: 'master') }
let!(:develop) { project.pipelines.create(source: :push, sha: commit.id, ref: 'develop') }
let!(:master) { project.pipelines.create(source: :push, sha: commit.id, ref: 'master', protected: false) }
let!(:develop) { project.pipelines.create(source: :push, sha: commit.id, ref: 'develop', protected: false) }
context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } }
......
......@@ -565,7 +565,7 @@ describe API::Commits do
end
context 'when the ref has a pipeline' do
let!(:pipeline) { project.pipelines.create(source: :push, ref: 'master', sha: commit.sha) }
let!(:pipeline) { project.pipelines.create(source: :push, ref: 'master', sha: commit.sha, protected: false) }
it 'includes a "created" status' do
get api(route, current_user)
......
......@@ -191,7 +191,8 @@ describe API::Runners do
active: !active,
tag_list: ['ruby2.1', 'pgsql', 'mysql'],
run_untagged: 'false',
locked: 'true')
locked: 'true',
access_level: 'ref_protected')
shared_runner.reload
expect(response).to have_http_status(200)
......@@ -200,6 +201,7 @@ describe API::Runners do
expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
expect(shared_runner.run_untagged?).to be(false)
expect(shared_runner.locked?).to be(true)
expect(shared_runner.ref_protected?).to be_truthy
expect(shared_runner.ensure_runner_queue_value)
.not_to eq(runner_queue_value)
end
......
......@@ -386,7 +386,7 @@ describe API::V3::Commits do
end
it "returns status for CI" do
pipeline = project.pipelines.create(source: :push, ref: 'master', sha: project.repository.commit.sha)
pipeline = project.pipelines.create(source: :push, ref: 'master', sha: project.repository.commit.sha, protected: false)
pipeline.update(status: 'success')
get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
......@@ -396,7 +396,7 @@ describe API::V3::Commits do
end
it "returns status for CI when pipeline is created" do
project.pipelines.create(source: :push, ref: 'master', sha: project.repository.commit.sha)
project.pipelines.create(source: :push, ref: 'master', sha: project.repository.commit.sha, protected: false)
get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
......
......@@ -391,12 +391,15 @@ describe Ci::CreatePipelineService do
end
context 'when user is master' do
let(:pipeline) { execute_service }
before do
project.add_master(user)
end
it 'creates a pipeline' do
expect(execute_service).to be_persisted
it 'creates a protected pipeline' do
expect(pipeline).to be_persisted
expect(pipeline).to be_protected
expect(Ci::Pipeline.count).to eq(1)
end
end
......@@ -468,10 +471,11 @@ describe Ci::CreatePipelineService do
let(:user) {}
let(:trigger) { create(:ci_trigger, owner: nil) }
let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) }
let(:pipeline) { execute_service(trigger_request: trigger_request) }
it 'creates a pipeline' do
expect(execute_service(trigger_request: trigger_request))
.to be_persisted
it 'creates an unprotected pipeline' do
expect(pipeline).to be_persisted
expect(pipeline).not_to be_protected
expect(Ci::Pipeline.count).to eq(1)
end
end
......
......@@ -4,7 +4,7 @@ module Ci
describe RegisterJobService do
let!(:project) { FactoryGirl.create :project, shared_runners_enabled: false }
let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
let!(:pending_build) { FactoryGirl.create :ci_build, pipeline: pipeline }
let!(:pending_job) { FactoryGirl.create :ci_build, pipeline: pipeline }
let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) }
let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) }
......@@ -15,32 +15,32 @@ module Ci
describe '#execute' do
context 'runner follow tag list' do
it "picks build with the same tag" do
pending_build.tag_list = ["linux"]
pending_build.save
pending_job.tag_list = ["linux"]
pending_job.save
specific_runner.tag_list = ["linux"]
expect(execute(specific_runner)).to eq(pending_build)
expect(execute(specific_runner)).to eq(pending_job)
end
it "does not pick build with different tag" do
pending_build.tag_list = ["linux"]
pending_build.save
pending_job.tag_list = ["linux"]
pending_job.save
specific_runner.tag_list = ["win32"]
expect(execute(specific_runner)).to be_falsey
end
it "picks build without tag" do
expect(execute(specific_runner)).to eq(pending_build)
expect(execute(specific_runner)).to eq(pending_job)
end
it "does not pick build with tag" do
pending_build.tag_list = ["linux"]
pending_build.save
pending_job.tag_list = ["linux"]
pending_job.save
expect(execute(specific_runner)).to be_falsey
end
it "pick build without tag" do
specific_runner.tag_list = ["win32"]
expect(execute(specific_runner)).to eq(pending_build)
expect(execute(specific_runner)).to eq(pending_job)
end
end
......@@ -76,7 +76,7 @@ module Ci
let!(:pipeline2) { create :ci_pipeline, project: project2 }
let!(:project3) { create :project, shared_runners_enabled: true }
let!(:pipeline3) { create :ci_pipeline, project: project3 }
let!(:build1_project1) { pending_build }
let!(:build1_project1) { pending_job }
let!(:build2_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
let!(:build3_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
let!(:build1_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
......@@ -172,7 +172,7 @@ module Ci
context 'when first build is stalled' do
before do
pending_build.lock_version = 10
pending_job.lock_version = 10
end
subject { described_class.new(specific_runner).execute }
......@@ -182,7 +182,7 @@ module Ci
before do
allow_any_instance_of(Ci::RegisterJobService).to receive(:builds_for_specific_runner)
.and_return([pending_build, other_build])
.and_return([pending_job, other_build])
end
it "receives second build from the queue" do
......@@ -194,7 +194,7 @@ module Ci
context 'when single build is in queue' do
before do
allow_any_instance_of(Ci::RegisterJobService).to receive(:builds_for_specific_runner)
.and_return([pending_build])
.and_return([pending_job])
end
it "does not receive any valid result" do
......@@ -215,6 +215,70 @@ module Ci
end
end
context 'when access_level of runner is not_protected' do
let!(:specific_runner) { create(:ci_runner, :specific) }
context 'when a job is protected' do
let!(:pending_job) { create(:ci_build, :protected, pipeline: pipeline) }
it 'picks the job' do
expect(execute(specific_runner)).to eq(pending_job)
end
end
context 'when a job is unprotected' do
let!(:pending_job) { create(:ci_build, pipeline: pipeline) }
it 'picks the job' do
expect(execute(specific_runner)).to eq(pending_job)
end
end
context 'when protected attribute of a job is nil' do
let!(:pending_job) { create(:ci_build, pipeline: pipeline) }
before do
pending_job.update_attribute(:protected, nil)
end
it 'picks the job' do
expect(execute(specific_runner)).to eq(pending_job)
end
end
end
context 'when access_level of runner is ref_protected' do
let!(:specific_runner) { create(:ci_runner, :ref_protected, :specific) }
context 'when a job is protected' do
let!(:pending_job) { create(:ci_build, :protected, pipeline: pipeline) }
it 'picks the job' do
expect(execute(specific_runner)).to eq(pending_job)
end
end
context 'when a job is unprotected' do
let!(:pending_job) { create(:ci_build, pipeline: pipeline) }
it 'does not pick the job' do
expect(execute(specific_runner)).to be_nil
end
end
context 'when protected attribute of a job is nil' do
let!(:pending_job) { create(:ci_build, pipeline: pipeline) }
before do
pending_job.update_attribute(:protected, nil)
end
it 'does not pick the job' do
expect(execute(specific_runner)).to be_nil
end
end
end
def execute(runner)
described_class.new(runner).execute.build
end
......
......@@ -48,7 +48,7 @@ describe Ci::RetryBuildService do
describe 'clone accessors' do
CLONE_ACCESSORS.each do |attribute|
it "clones #{attribute} build attribute" do
expect(new_build.send(attribute)).to be_present
expect(new_build.send(attribute)).not_to be_nil
expect(new_build.send(attribute)).to eq build.send(attribute)
end
end
......
......@@ -80,7 +80,8 @@ module CycleAnalyticsHelpers
sha: project.repository.commit('master').sha,
ref: 'master',
source: :push,
project: project)
project: project,
protected: false)
end
def new_dummy_job(environment)
......@@ -93,7 +94,8 @@ module CycleAnalyticsHelpers
ref: 'master',
tag: false,
name: 'dummy',
pipeline: dummy_pipeline)
pipeline: dummy_pipeline,
protected: false)
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