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

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
......@@ -3,6 +3,7 @@ class Build < CommitStatus
include TokenAuthenticatable
include AfterCommitQueue
include Presentable
include Importable
belongs_to :runner
belongs_to :trigger_request
......@@ -26,6 +27,7 @@ def persisted_environment
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 @@ def persisted_environment
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 @@ class Pipeline < ActiveRecord::Base
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 @@ class Runner < ActiveRecord::Base
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 @@ class Runner < ActiveRecord::Base
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 @@ def specific?
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 @@ def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request
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 @@ def running_builds_for_shared_runners
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 @@ class RetryBuildService < ::BaseService
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 @@
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 @@
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 @@
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 @@
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 @@ class CommitStatuses < Grape::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 @@ class CommitStatuses < Grape::API
pipeline: pipeline,
name: name,
ref: ref,
user: current_user
user: current_user,
protected: @project.protected_for?(ref)
)
optional_attributes =
......
......@@ -775,6 +775,7 @@ class RunnerDetails < Runner
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? }
......
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