Merge branch 'feature/runner-lock-on-project' into 'master'
Make it possible to lock runner on a specific project Make it possible to lock runner on a specific project. ![Screen_Shot_2016-06-20_at_4.03.08_PM](/uploads/186378643a20106ff0b67b6fd8bd7f28/Screen_Shot_2016-06-20_at_4.03.08_PM.png) ---- ![Screen_Shot_2016-06-20_at_9.54.52_PM](/uploads/c479abdffaf19f383bb6b5a42bdd6cc3/Screen_Shot_2016-06-20_at_9.54.52_PM.png) ---- ![Screen_Shot_2016-06-20_at_9.56.26_PM](/uploads/6ad838679b0c28a1fe2e20e9224387ea/Screen_Shot_2016-06-20_at_9.56.26_PM.png) Closes #3407 See merge request !4093
This commit is contained in:
commit
f90c8c624d
|
@ -71,6 +71,7 @@ v 8.9.0 (unreleased)
|
||||||
- Todos will display target state if issuable target is 'Closed' or 'Merged'
|
- Todos will display target state if issuable target is 'Closed' or 'Merged'
|
||||||
- Validate only and except regexp
|
- Validate only and except regexp
|
||||||
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
|
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
|
||||||
|
- POST to API /projects/:id/runners/:runner_id would give 409 if the runner was already enabled for this project
|
||||||
- Add support for using Yubikeys (U2F) for two-factor authentication
|
- Add support for using Yubikeys (U2F) for two-factor authentication
|
||||||
- Link to blank group icon doesn't throw a 404 anymore
|
- Link to blank group icon doesn't throw a 404 anymore
|
||||||
- Remove 'main language' feature
|
- Remove 'main language' feature
|
||||||
|
@ -86,6 +87,7 @@ v 8.9.0 (unreleased)
|
||||||
- Make Omniauth providers specs to not modify global configuration
|
- Make Omniauth providers specs to not modify global configuration
|
||||||
- Remove unused JiraIssue class and replace references with ExternalIssue. !4659 (Ilan Shamir)
|
- Remove unused JiraIssue class and replace references with ExternalIssue. !4659 (Ilan Shamir)
|
||||||
- Make authentication service for Container Registry to be compatible with < Docker 1.11
|
- Make authentication service for Container Registry to be compatible with < Docker 1.11
|
||||||
|
- Make it possible to lock a runner from being enabled for other projects
|
||||||
- Add Application Setting to configure Container Registry token expire delay (default 5min)
|
- Add Application Setting to configure Container Registry token expire delay (default 5min)
|
||||||
- Cache assigned issue and merge request counts in sidebar nav
|
- Cache assigned issue and merge request counts in sidebar nav
|
||||||
- Use Knapsack only in CI environment
|
- Use Knapsack only in CI environment
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
class Admin::RunnerProjectsController < Admin::ApplicationController
|
class Admin::RunnerProjectsController < Admin::ApplicationController
|
||||||
before_action :project, only: [:create]
|
before_action :project, only: [:create]
|
||||||
|
|
||||||
def index
|
|
||||||
@runner_projects = project.runner_projects.all
|
|
||||||
@runner_project = project.runner_projects.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||||
|
|
||||||
if @runner.assign_to(@project, current_user)
|
return head(403) if @runner.is_shared? || @runner.locked?
|
||||||
|
|
||||||
|
runner_project = @runner.assign_to(@project, current_user)
|
||||||
|
|
||||||
|
if runner_project.persisted?
|
||||||
redirect_to admin_runner_path(@runner)
|
redirect_to admin_runner_path(@runner)
|
||||||
else
|
else
|
||||||
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
|
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
|
||||||
|
|
|
@ -6,11 +6,13 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
|
||||||
def create
|
def create
|
||||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||||
|
|
||||||
|
return head(403) if @runner.is_shared? || @runner.locked?
|
||||||
return head(403) unless current_user.ci_authorized_runners.include?(@runner)
|
return head(403) unless current_user.ci_authorized_runners.include?(@runner)
|
||||||
|
|
||||||
path = runners_path(project)
|
path = runners_path(project)
|
||||||
|
runner_project = @runner.assign_to(project, current_user)
|
||||||
|
|
||||||
if @runner.assign_to(project, current_user)
|
if runner_project.persisted?
|
||||||
redirect_to path
|
redirect_to path
|
||||||
else
|
else
|
||||||
redirect_to path, alert: 'Failed adding runner to project'
|
redirect_to path, alert: 'Failed adding runner to project'
|
||||||
|
|
|
@ -5,10 +5,9 @@ class Projects::RunnersController < Projects::ApplicationController
|
||||||
layout 'project_settings'
|
layout 'project_settings'
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@runners = project.runners.ordered
|
@project_runners = project.runners.ordered
|
||||||
@specific_runners = current_user.ci_authorized_runners.
|
@assignable_runners = current_user.ci_authorized_runners.
|
||||||
where.not(id: project.runners).
|
assignable_for(project).ordered.page(params[:page]).per(20)
|
||||||
ordered.page(params[:page]).per(20)
|
|
||||||
@shared_runners = Ci::Runner.shared.active
|
@shared_runners = Ci::Runner.shared.active
|
||||||
@shared_runners_count = @shared_runners.count(:all)
|
@shared_runners_count = @shared_runners.count(:all)
|
||||||
end
|
end
|
||||||
|
|
|
@ -300,18 +300,12 @@ module Ci
|
||||||
project.valid_runners_token? token
|
project.valid_runners_token? token
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_be_served?(runner)
|
|
||||||
return false unless has_tags? || runner.run_untagged?
|
|
||||||
|
|
||||||
(tag_list - runner.tag_list).empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_tags?
|
def has_tags?
|
||||||
tag_list.any?
|
tag_list.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
def any_runners_online?
|
def any_runners_online?
|
||||||
project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
|
project.any_runners? { |runner| runner.active? && runner.online? && runner.can_pick?(self) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def stuck?
|
def stuck?
|
||||||
|
|
|
@ -4,7 +4,7 @@ module Ci
|
||||||
|
|
||||||
LAST_CONTACT_TIME = 5.minutes.ago
|
LAST_CONTACT_TIME = 5.minutes.ago
|
||||||
AVAILABLE_SCOPES = %w[specific shared active paused online]
|
AVAILABLE_SCOPES = %w[specific shared active paused online]
|
||||||
FORM_EDITABLE = %i[description tag_list active run_untagged]
|
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
|
||||||
|
|
||||||
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'
|
||||||
|
@ -26,6 +26,13 @@ module Ci
|
||||||
.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)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope :assignable_for, ->(project) do
|
||||||
|
# FIXME: That `to_sql` is needed to workaround a weird Rails bug.
|
||||||
|
# Without that, placeholders would miss one and couldn't match.
|
||||||
|
where(locked: false).
|
||||||
|
where.not("id IN (#{project.runners.select(:id).to_sql})").specific
|
||||||
|
end
|
||||||
|
|
||||||
validate :tag_constraints
|
validate :tag_constraints
|
||||||
|
|
||||||
acts_as_taggable
|
acts_as_taggable
|
||||||
|
@ -56,7 +63,7 @@ module Ci
|
||||||
def assign_to(project, current_user = nil)
|
def assign_to(project, current_user = nil)
|
||||||
self.is_shared = false if shared?
|
self.is_shared = false if shared?
|
||||||
self.save
|
self.save
|
||||||
project.runner_projects.create!(runner_id: self.id)
|
project.runner_projects.create(runner_id: self.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def display_name
|
def display_name
|
||||||
|
@ -91,6 +98,10 @@ module Ci
|
||||||
!shared?
|
!shared?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_pick?(build)
|
||||||
|
assignable_for?(build.project) && accepting_tags?(build)
|
||||||
|
end
|
||||||
|
|
||||||
def only_for?(project)
|
def only_for?(project)
|
||||||
projects == [project]
|
projects == [project]
|
||||||
end
|
end
|
||||||
|
@ -111,5 +122,13 @@ module Ci
|
||||||
'can not be empty when runner is not allowed to pick untagged jobs')
|
'can not be empty when runner is not allowed to pick untagged jobs')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def assignable_for?(project)
|
||||||
|
!locked? || projects.exists?(id: project.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def accepting_tags?(build)
|
||||||
|
(run_untagged? || build.has_tags?) && (build.tag_list - tag_list).empty?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
build = builds.find do |build|
|
build = builds.find do |build|
|
||||||
build.can_be_served?(current_runner)
|
current_runner.can_pick?(build)
|
||||||
end
|
end
|
||||||
|
|
||||||
if build
|
if build
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
.col-md-6
|
.col-md-6
|
||||||
%h4 Restrict projects for this runner
|
%h4 Restrict projects for this runner
|
||||||
- if @runner.projects.any?
|
- if @runner.projects.any?
|
||||||
%table.table
|
%table.table.assigned-projects
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Assigned projects
|
%th Assigned projects
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
.pull-right
|
.pull-right
|
||||||
= link_to 'Disable', [:admin, project.namespace.becomes(Namespace), project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
|
= link_to 'Disable', [:admin, project.namespace.becomes(Namespace), project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
|
||||||
|
|
||||||
%table.table
|
%table.table.unassigned-projects
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Project
|
%th Project
|
||||||
|
|
|
@ -12,6 +12,12 @@
|
||||||
.checkbox
|
.checkbox
|
||||||
= f.check_box :run_untagged
|
= f.check_box :run_untagged
|
||||||
%span.light Indicates whether this runner can pick jobs without tags
|
%span.light Indicates whether this runner can pick jobs without tags
|
||||||
|
.form-group
|
||||||
|
= label :locked, 'Lock to current projects', class: 'control-label'
|
||||||
|
.col-sm-10
|
||||||
|
.checkbox
|
||||||
|
= f.check_box :locked
|
||||||
|
%span.light When a runner is locked, it cannot be assigned to other projects
|
||||||
.form-group
|
.form-group
|
||||||
= label_tag :token, class: 'control-label' do
|
= label_tag :token, class: 'control-label' do
|
||||||
Token
|
Token
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
%h4
|
%h4
|
||||||
= runner_status_icon(runner)
|
= runner_status_icon(runner)
|
||||||
%span.monospace
|
%span.monospace
|
||||||
- if @runners.include?(runner)
|
- if @project_runners.include?(runner)
|
||||||
= link_to runner.short_sha, runner_path(runner)
|
= link_to runner.short_sha, runner_path(runner)
|
||||||
|
- if runner.locked?
|
||||||
|
= icon('lock', class: 'has-tooltip', title: 'Locked to current projects')
|
||||||
%small
|
%small
|
||||||
= link_to edit_namespace_project_runner_path(@project.namespace, @project, runner) do
|
= link_to edit_namespace_project_runner_path(@project.namespace, @project, runner) do
|
||||||
%i.fa.fa-edit.btn
|
%i.fa.fa-edit.btn
|
||||||
|
@ -11,7 +13,7 @@
|
||||||
= runner.short_sha
|
= runner.short_sha
|
||||||
|
|
||||||
.pull-right
|
.pull-right
|
||||||
- if @runners.include?(runner)
|
- if @project_runners.include?(runner)
|
||||||
- if runner.belongs_to_one_project?
|
- if runner.belongs_to_one_project?
|
||||||
= link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm'
|
= link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm'
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
Start runner!
|
Start runner!
|
||||||
|
|
||||||
|
|
||||||
- if @runners.any?
|
- if @project_runners.any?
|
||||||
%h4.underlined-title Runners activated for this project
|
%h4.underlined-title Runners activated for this project
|
||||||
%ul.bordered-list.activated-specific-runners
|
%ul.bordered-list.activated-specific-runners
|
||||||
= render partial: 'runner', collection: @runners, as: :runner
|
= render partial: 'runner', collection: @project_runners, as: :runner
|
||||||
|
|
||||||
- if @specific_runners.any?
|
- if @assignable_runners.any?
|
||||||
%h4.underlined-title Available specific runners
|
%h4.underlined-title Available specific runners
|
||||||
%ul.bordered-list.available-specific-runners
|
%ul.bordered-list.available-specific-runners
|
||||||
= render partial: 'runner', collection: @specific_runners, as: :runner
|
= render partial: 'runner', collection: @assignable_runners, as: :runner
|
||||||
= paginate @specific_runners
|
= paginate @assignable_runners
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
%tr
|
%tr
|
||||||
%td Can run untagged jobs
|
%td Can run untagged jobs
|
||||||
%td= @runner.run_untagged? ? 'Yes' : 'No'
|
%td= @runner.run_untagged? ? 'Yes' : 'No'
|
||||||
|
%tr
|
||||||
|
%td Locked to this project
|
||||||
|
%td= @runner.locked? ? 'Yes' : 'No'
|
||||||
%tr
|
%tr
|
||||||
%td Tags
|
%td Tags
|
||||||
%td
|
%td
|
||||||
|
|
|
@ -295,7 +295,7 @@ Rails.application.routes.draw do
|
||||||
post :repository_check
|
post :repository_check
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :runner_projects
|
resources :runner_projects, only: [:create, :destroy]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
class AddLockedToCiRunner < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column_with_default(:ci_runners, :locked, :boolean,
|
||||||
|
default: false, allow_null: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column(:ci_runners, :locked)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class AddIndexOnRunnersLocked < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_concurrent_index :ci_runners, :locked
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20160617301627) do
|
ActiveRecord::Schema.define(version: 20160620115026) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -287,9 +287,11 @@ ActiveRecord::Schema.define(version: 20160617301627) do
|
||||||
t.string "platform"
|
t.string "platform"
|
||||||
t.string "architecture"
|
t.string "architecture"
|
||||||
t.boolean "run_untagged", default: true, null: false
|
t.boolean "run_untagged", default: true, null: false
|
||||||
|
t.boolean "locked", default: false, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "ci_runners", ["description"], name: "index_ci_runners_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
|
add_index "ci_runners", ["description"], name: "index_ci_runners_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
|
||||||
|
add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree
|
||||||
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
|
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
|
||||||
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token_trigram", using: :gin, opclasses: {"token"=>"gin_trgm_ops"}
|
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token_trigram", using: :gin, opclasses: {"token"=>"gin_trgm_ops"}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,12 @@ To register the runner, run the command below and follow instructions:
|
||||||
sudo gitlab-ci-multi-runner register
|
sudo gitlab-ci-multi-runner register
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Lock a specific runner from being enabled for other projects
|
||||||
|
|
||||||
|
You can configure a runner to assign it exclusively to a project. When a
|
||||||
|
runner is locked this way, it can no longer be enabled for other projects.
|
||||||
|
This setting is available on each runner in *Project Settings* > *Runners*.
|
||||||
|
|
||||||
### Making an existing Shared Runner Specific
|
### Making an existing Shared Runner Specific
|
||||||
|
|
||||||
If you are an admin on your GitLab instance,
|
If you are an admin on your GitLab instance,
|
||||||
|
@ -128,7 +134,7 @@ the appropriate dependencies to run Rails test suites.
|
||||||
### Prevent runner with tags from picking jobs without tags
|
### Prevent runner with tags from picking jobs without tags
|
||||||
|
|
||||||
You can configure a runner to prevent it from picking jobs with tags when
|
You can configure a runner to prevent it from picking jobs with tags when
|
||||||
the runnner does not have tags assigned. This setting is available on each
|
the runner does not have tags assigned. This setting is available on each
|
||||||
runner in *Project Settings* > *Runners*.
|
runner in *Project Settings* > *Runners*.
|
||||||
|
|
||||||
### Be careful with sensitive information
|
### Be careful with sensitive information
|
||||||
|
|
|
@ -423,6 +423,7 @@ module API
|
||||||
class RunnerDetails < Runner
|
class RunnerDetails < Runner
|
||||||
expose :tag_list
|
expose :tag_list
|
||||||
expose :run_untagged
|
expose :run_untagged
|
||||||
|
expose :locked
|
||||||
expose :version, :revision, :platform, :architecture
|
expose :version, :revision, :platform, :architecture
|
||||||
expose :contacted_at
|
expose :contacted_at
|
||||||
expose :token, if: lambda { |runner, options| options[:current_user].is_admin? || !runner.is_shared? }
|
expose :token, if: lambda { |runner, options| options[:current_user].is_admin? || !runner.is_shared? }
|
||||||
|
|
|
@ -49,7 +49,7 @@ module API
|
||||||
runner = get_runner(params[:id])
|
runner = get_runner(params[:id])
|
||||||
authenticate_update_runner!(runner)
|
authenticate_update_runner!(runner)
|
||||||
|
|
||||||
attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged]
|
attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged, :locked]
|
||||||
if runner.update(attrs)
|
if runner.update(attrs)
|
||||||
present runner, with: Entities::RunnerDetails, current_user: current_user
|
present runner, with: Entities::RunnerDetails, current_user: current_user
|
||||||
else
|
else
|
||||||
|
@ -96,9 +96,14 @@ module API
|
||||||
|
|
||||||
runner = get_runner(params[:runner_id])
|
runner = get_runner(params[:runner_id])
|
||||||
authenticate_enable_runner!(runner)
|
authenticate_enable_runner!(runner)
|
||||||
Ci::RunnerProject.create(runner: runner, project: user_project)
|
|
||||||
|
|
||||||
|
runner_project = runner.assign_to(user_project)
|
||||||
|
|
||||||
|
if runner_project.persisted?
|
||||||
present runner, with: Entities::Runner
|
present runner, with: Entities::Runner
|
||||||
|
else
|
||||||
|
conflict!("Runner was already enabled for this project")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Disable project's runner
|
# Disable project's runner
|
||||||
|
@ -163,6 +168,7 @@ module API
|
||||||
|
|
||||||
def authenticate_enable_runner!(runner)
|
def authenticate_enable_runner!(runner)
|
||||||
forbidden!("Runner is shared") if runner.is_shared?
|
forbidden!("Runner is shared") if runner.is_shared?
|
||||||
|
forbidden!("Runner is locked") if runner.locked?
|
||||||
return if current_user.is_admin?
|
return if current_user.is_admin?
|
||||||
forbidden!("No access granted") unless user_can_access_runner?(runner)
|
forbidden!("No access granted") unless user_can_access_runner?(runner)
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,12 +28,9 @@ module Ci
|
||||||
post "register" do
|
post "register" do
|
||||||
required_attributes! [:token]
|
required_attributes! [:token]
|
||||||
|
|
||||||
attributes = { description: params[:description],
|
attributes = attributes_for_keys(
|
||||||
tag_list: params[:tag_list] }
|
[:description, :tag_list, :run_untagged, :locked]
|
||||||
|
)
|
||||||
unless params[:run_untagged].nil?
|
|
||||||
attributes[:run_untagged] = params[:run_untagged]
|
|
||||||
end
|
|
||||||
|
|
||||||
runner =
|
runner =
|
||||||
if runner_registration_token_valid?
|
if runner_registration_token_valid?
|
||||||
|
|
|
@ -60,6 +60,40 @@ describe "Admin Runners" do
|
||||||
it { expect(page).to have_content(@project1.name_with_namespace) }
|
it { expect(page).to have_content(@project1.name_with_namespace) }
|
||||||
it { expect(page).not_to have_content(@project2.name_with_namespace) }
|
it { expect(page).not_to have_content(@project2.name_with_namespace) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'enable/create' do
|
||||||
|
before do
|
||||||
|
@project1.runners << runner
|
||||||
|
visit admin_runner_path(runner)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'enables specific runner for project' do
|
||||||
|
within '.unassigned-projects' do
|
||||||
|
click_on 'Enable'
|
||||||
|
end
|
||||||
|
|
||||||
|
assigned_project = page.find('.assigned-projects')
|
||||||
|
|
||||||
|
expect(assigned_project).to have_content(@project2.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'disable/destroy' do
|
||||||
|
before do
|
||||||
|
@project1.runners << runner
|
||||||
|
visit admin_runner_path(runner)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'enables specific runner for project' do
|
||||||
|
within '.assigned-projects' do
|
||||||
|
click_on 'Disable'
|
||||||
|
end
|
||||||
|
|
||||||
|
new_runner_project = page.find('.unassigned-projects')
|
||||||
|
|
||||||
|
expect(new_runner_project).to have_content(@project1.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'runners registration token' do
|
describe 'runners registration token' do
|
||||||
|
|
|
@ -36,32 +36,44 @@ describe Ci::Build, models: true do
|
||||||
subject { build.ignored? }
|
subject { build.ignored? }
|
||||||
|
|
||||||
context 'if build is not allowed to fail' do
|
context 'if build is not allowed to fail' do
|
||||||
before { build.allow_failure = false }
|
before do
|
||||||
|
build.allow_failure = false
|
||||||
|
end
|
||||||
|
|
||||||
context 'and build.status is success' do
|
context 'and build.status is success' do
|
||||||
before { build.status = 'success' }
|
before do
|
||||||
|
build.status = 'success'
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
it { is_expected.to be_falsey }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'and build.status is failed' do
|
context 'and build.status is failed' do
|
||||||
before { build.status = 'failed' }
|
before do
|
||||||
|
build.status = 'failed'
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
it { is_expected.to be_falsey }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'if build is allowed to fail' do
|
context 'if build is allowed to fail' do
|
||||||
before { build.allow_failure = true }
|
before do
|
||||||
|
build.allow_failure = true
|
||||||
|
end
|
||||||
|
|
||||||
context 'and build.status is success' do
|
context 'and build.status is success' do
|
||||||
before { build.status = 'success' }
|
before do
|
||||||
|
build.status = 'success'
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
it { is_expected.to be_falsey }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'and build.status is failed' do
|
context 'and build.status is failed' do
|
||||||
before { build.status = 'failed' }
|
before do
|
||||||
|
build.status = 'failed'
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_truthy }
|
it { is_expected.to be_truthy }
|
||||||
end
|
end
|
||||||
|
@ -75,7 +87,9 @@ describe Ci::Build, models: true do
|
||||||
|
|
||||||
context 'if build.trace contains text' do
|
context 'if build.trace contains text' do
|
||||||
let(:text) { 'example output' }
|
let(:text) { 'example output' }
|
||||||
before { build.trace = text }
|
before do
|
||||||
|
build.trace = text
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to include(text) }
|
it { is_expected.to include(text) }
|
||||||
it { expect(subject.length).to be >= text.length }
|
it { expect(subject.length).to be >= text.length }
|
||||||
|
@ -188,7 +202,9 @@ describe Ci::Build, models: true do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
before { build.update_attributes(stage: 'stage') }
|
before do
|
||||||
|
build.update_attributes(stage: 'stage')
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(predefined_variables + yaml_variables) }
|
it { is_expected.to eq(predefined_variables + yaml_variables) }
|
||||||
|
|
||||||
|
@ -199,7 +215,9 @@ describe Ci::Build, models: true do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
before { build.update_attributes(tag: true) }
|
before do
|
||||||
|
build.update_attributes(tag: true)
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
|
it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
|
||||||
end
|
end
|
||||||
|
@ -257,57 +275,6 @@ describe Ci::Build, models: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#can_be_served?' do
|
|
||||||
let(:runner) { create(:ci_runner) }
|
|
||||||
|
|
||||||
before { build.project.runners << runner }
|
|
||||||
|
|
||||||
context 'when runner does not have 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 'when runner has tags' do
|
|
||||||
before { runner.tag_list = ['bb', 'cc'] }
|
|
||||||
|
|
||||||
shared_examples 'tagged build picker' do
|
|
||||||
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 without matching tags' do
|
|
||||||
build.tag_list = ['aa']
|
|
||||||
expect(build.can_be_served?(runner)).to be_falsey
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when runner can pick untagged jobs' do
|
|
||||||
it 'can handle builds without tags' do
|
|
||||||
expect(build.can_be_served?(runner)).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'tagged build picker'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when runner can not pick untagged jobs' do
|
|
||||||
before { runner.run_untagged = false }
|
|
||||||
|
|
||||||
it 'can not handle builds without tags' do
|
|
||||||
expect(build.can_be_served?(runner)).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'tagged build picker'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#has_tags?' do
|
describe '#has_tags?' do
|
||||||
context 'when build has tags' do
|
context 'when build has tags' do
|
||||||
subject { create(:ci_build, tag_list: ['tag']) }
|
subject { create(:ci_build, tag_list: ['tag']) }
|
||||||
|
@ -348,7 +315,7 @@ describe Ci::Build, models: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'that cannot handle build' do
|
it 'that cannot handle build' do
|
||||||
expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false)
|
expect_any_instance_of(Ci::Runner).to receive(:can_pick?).and_return(false)
|
||||||
is_expected.to be_falsey
|
is_expected.to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -360,7 +327,9 @@ describe Ci::Build, models: true do
|
||||||
|
|
||||||
%w(pending).each do |state|
|
%w(pending).each do |state|
|
||||||
context "if commit_status.status is #{state}" do
|
context "if commit_status.status is #{state}" do
|
||||||
before { build.status = state }
|
before do
|
||||||
|
build.status = state
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_truthy }
|
it { is_expected.to be_truthy }
|
||||||
|
|
||||||
|
@ -379,7 +348,9 @@ describe Ci::Build, models: true do
|
||||||
|
|
||||||
%w(success failed canceled running).each do |state|
|
%w(success failed canceled running).each do |state|
|
||||||
context "if commit_status.status is #{state}" do
|
context "if commit_status.status is #{state}" do
|
||||||
before { build.status = state }
|
before do
|
||||||
|
build.status = state
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
it { is_expected.to be_falsey }
|
||||||
end
|
end
|
||||||
|
@ -390,7 +361,10 @@ describe Ci::Build, models: true do
|
||||||
subject { build.artifacts? }
|
subject { build.artifacts? }
|
||||||
|
|
||||||
context 'artifacts archive does not exist' do
|
context 'artifacts archive does not exist' do
|
||||||
before { build.update_attributes(artifacts_file: nil) }
|
before do
|
||||||
|
build.update_attributes(artifacts_file: nil)
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsy }
|
it { is_expected.to be_falsy }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -623,7 +597,9 @@ describe Ci::Build, models: true do
|
||||||
let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
|
let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
|
||||||
|
|
||||||
describe '#erase' do
|
describe '#erase' do
|
||||||
before { build.erase(erased_by: user) }
|
before do
|
||||||
|
build.erase(erased_by: user)
|
||||||
|
end
|
||||||
|
|
||||||
context 'erased by user' do
|
context 'erased by user' do
|
||||||
let!(:user) { create(:user, username: 'eraser') }
|
let!(:user) { create(:user, username: 'eraser') }
|
||||||
|
@ -660,7 +636,9 @@ describe Ci::Build, models: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'build has been erased' do
|
context 'build has been erased' do
|
||||||
before { build.erase }
|
before do
|
||||||
|
build.erase
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be true }
|
it { is_expected.to be true }
|
||||||
end
|
end
|
||||||
|
@ -668,7 +646,9 @@ describe Ci::Build, models: true do
|
||||||
|
|
||||||
context 'metadata and build trace are not available' do
|
context 'metadata and build trace are not available' do
|
||||||
let!(:build) { create(:ci_build, :success, :artifacts) }
|
let!(:build) { create(:ci_build, :success, :artifacts) }
|
||||||
before { build.remove_artifacts_metadata! }
|
before do
|
||||||
|
build.remove_artifacts_metadata!
|
||||||
|
end
|
||||||
|
|
||||||
describe '#erase' do
|
describe '#erase' do
|
||||||
it 'should not raise error' do
|
it 'should not raise error' do
|
||||||
|
|
|
@ -20,34 +20,36 @@ describe Ci::Runner, models: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#display_name' do
|
describe '#display_name' do
|
||||||
it 'should return the description if it has a value' do
|
it 'returns the description if it has a value' do
|
||||||
runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
|
runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
|
||||||
expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448'
|
expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the token if it does not have a description' do
|
it 'returns the token if it does not have a description' do
|
||||||
runner = FactoryGirl.create(:ci_runner)
|
runner = FactoryGirl.create(:ci_runner)
|
||||||
expect(runner.display_name).to eq runner.description
|
expect(runner.display_name).to eq runner.description
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the token if the description is an empty string' do
|
it 'returns the token if the description is an empty string' do
|
||||||
runner = FactoryGirl.build(:ci_runner, description: '', token: 'token')
|
runner = FactoryGirl.build(:ci_runner, description: '', token: 'token')
|
||||||
expect(runner.display_name).to eq runner.token
|
expect(runner.display_name).to eq runner.token
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe :assign_to do
|
describe '#assign_to' do
|
||||||
let!(:project) { FactoryGirl.create :empty_project }
|
let!(:project) { FactoryGirl.create :empty_project }
|
||||||
let!(:shared_runner) { FactoryGirl.create(:ci_runner, :shared) }
|
let!(:shared_runner) { FactoryGirl.create(:ci_runner, :shared) }
|
||||||
|
|
||||||
before { shared_runner.assign_to(project) }
|
before do
|
||||||
|
shared_runner.assign_to(project)
|
||||||
|
end
|
||||||
|
|
||||||
it { expect(shared_runner).to be_specific }
|
it { expect(shared_runner).to be_specific }
|
||||||
it { expect(shared_runner.projects).to eq([project]) }
|
it { expect(shared_runner.projects).to eq([project]) }
|
||||||
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
|
describe '.online' do
|
||||||
subject { Ci::Runner.online }
|
subject { Ci::Runner.online }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -58,60 +60,269 @@ describe Ci::Runner, models: true do
|
||||||
it { is_expected.to eq([@runner2])}
|
it { is_expected.to eq([@runner2])}
|
||||||
end
|
end
|
||||||
|
|
||||||
describe :online? do
|
describe '#online?' do
|
||||||
let(:runner) { FactoryGirl.create(:ci_runner, :shared) }
|
let(:runner) { FactoryGirl.create(:ci_runner, :shared) }
|
||||||
|
|
||||||
subject { runner.online? }
|
subject { runner.online? }
|
||||||
|
|
||||||
context 'never contacted' do
|
context 'never contacted' do
|
||||||
before { runner.contacted_at = nil }
|
before do
|
||||||
|
runner.contacted_at = nil
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
it { is_expected.to be_falsey }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'contacted long time ago time' do
|
context 'contacted long time ago time' do
|
||||||
before { runner.contacted_at = 1.year.ago }
|
before do
|
||||||
|
runner.contacted_at = 1.year.ago
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
it { is_expected.to be_falsey }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'contacted 1s ago' do
|
context 'contacted 1s ago' do
|
||||||
before { runner.contacted_at = 1.second.ago }
|
before do
|
||||||
|
runner.contacted_at = 1.second.ago
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to be_truthy }
|
it { is_expected.to be_truthy }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe :status do
|
describe '#can_pick?' do
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||||
|
let(:build) { create(:ci_build, pipeline: pipeline) }
|
||||||
|
let(:runner) { create(:ci_runner) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
build.project.runners << runner
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when runner does not have tags' do
|
||||||
|
it 'can handle builds without tags' do
|
||||||
|
expect(runner.can_pick?(build)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle build with tags' do
|
||||||
|
build.tag_list = ['aa']
|
||||||
|
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when runner has tags' do
|
||||||
|
before do
|
||||||
|
runner.tag_list = ['bb', 'cc']
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'tagged build picker' do
|
||||||
|
it 'can handle build with matching tags' do
|
||||||
|
build.tag_list = ['bb']
|
||||||
|
|
||||||
|
expect(runner.can_pick?(build)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle build without matching tags' do
|
||||||
|
build.tag_list = ['aa']
|
||||||
|
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when runner can pick untagged jobs' do
|
||||||
|
it 'can handle builds without tags' do
|
||||||
|
expect(runner.can_pick?(build)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'tagged build picker'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when runner cannot pick untagged jobs' do
|
||||||
|
before do
|
||||||
|
runner.run_untagged = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle builds without tags' do
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'tagged build picker'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when runner is locked' do
|
||||||
|
before do
|
||||||
|
runner.locked = true
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'locked build picker' do
|
||||||
|
context 'when runner cannot pick untagged jobs' do
|
||||||
|
before do
|
||||||
|
runner.run_untagged = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle builds without tags' do
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when having runner tags' do
|
||||||
|
before do
|
||||||
|
runner.tag_list = ['bb', 'cc']
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle it for builds without matching tags' do
|
||||||
|
build.tag_list = ['aa']
|
||||||
|
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when serving the same project' do
|
||||||
|
it 'can handle it' do
|
||||||
|
expect(runner.can_pick?(build)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'locked build picker'
|
||||||
|
|
||||||
|
context 'when having runner tags' do
|
||||||
|
before do
|
||||||
|
runner.tag_list = ['bb', 'cc']
|
||||||
|
build.tag_list = ['bb']
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can handle it for matching tags' do
|
||||||
|
expect(runner.can_pick?(build)).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'serving a different project' do
|
||||||
|
before do
|
||||||
|
runner.runner_projects.destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle it' do
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'locked build picker'
|
||||||
|
|
||||||
|
context 'when having runner tags' do
|
||||||
|
before do
|
||||||
|
runner.tag_list = ['bb', 'cc']
|
||||||
|
build.tag_list = ['bb']
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot handle it for matching tags' do
|
||||||
|
expect(runner.can_pick?(build)).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#status' do
|
||||||
let(:runner) { FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.second.ago) }
|
let(:runner) { FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.second.ago) }
|
||||||
|
|
||||||
subject { runner.status }
|
subject { runner.status }
|
||||||
|
|
||||||
context 'never connected' do
|
context 'never connected' do
|
||||||
before { runner.contacted_at = nil }
|
before do
|
||||||
|
runner.contacted_at = nil
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(:not_connected) }
|
it { is_expected.to eq(:not_connected) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'contacted 1s ago' do
|
context 'contacted 1s ago' do
|
||||||
before { runner.contacted_at = 1.second.ago }
|
before do
|
||||||
|
runner.contacted_at = 1.second.ago
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(:online) }
|
it { is_expected.to eq(:online) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'contacted long time ago' do
|
context 'contacted long time ago' do
|
||||||
before { runner.contacted_at = 1.year.ago }
|
before do
|
||||||
|
runner.contacted_at = 1.year.ago
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(:offline) }
|
it { is_expected.to eq(:offline) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'inactive' do
|
context 'inactive' do
|
||||||
before { runner.active = false }
|
before do
|
||||||
|
runner.active = false
|
||||||
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(:paused) }
|
it { is_expected.to eq(:paused) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.assignable_for' do
|
||||||
|
let(:runner) { create(:ci_runner) }
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
let(:another_project) { create(:project) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.runners << runner
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with shared runners' do
|
||||||
|
before do
|
||||||
|
runner.update(is_shared: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'does not give owned runner' do
|
||||||
|
subject { Ci::Runner.assignable_for(project) }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'does not give shared runner' do
|
||||||
|
subject { Ci::Runner.assignable_for(another_project) }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with unlocked runner' do
|
||||||
|
context 'does not give owned runner' do
|
||||||
|
subject { Ci::Runner.assignable_for(project) }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'does give a specific runner' do
|
||||||
|
subject { Ci::Runner.assignable_for(another_project) }
|
||||||
|
|
||||||
|
it { is_expected.to contain_exactly(runner) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with locked runner' do
|
||||||
|
before do
|
||||||
|
runner.update(locked: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'does not give owned runner' do
|
||||||
|
subject { Ci::Runner.assignable_for(project) }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'does not give a locked runner' do
|
||||||
|
subject { Ci::Runner.assignable_for(another_project) }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
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_runner)
|
runner = FactoryGirl.create(:ci_runner)
|
||||||
|
|
|
@ -187,14 +187,16 @@ describe API::Runners, api: true do
|
||||||
update_runner(shared_runner.id, admin, description: "#{description}_updated",
|
update_runner(shared_runner.id, admin, description: "#{description}_updated",
|
||||||
active: !active,
|
active: !active,
|
||||||
tag_list: ['ruby2.1', 'pgsql', 'mysql'],
|
tag_list: ['ruby2.1', 'pgsql', 'mysql'],
|
||||||
run_untagged: 'false')
|
run_untagged: 'false',
|
||||||
|
locked: 'true')
|
||||||
shared_runner.reload
|
shared_runner.reload
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(shared_runner.description).to eq("#{description}_updated")
|
expect(shared_runner.description).to eq("#{description}_updated")
|
||||||
expect(shared_runner.active).to eq(!active)
|
expect(shared_runner.active).to eq(!active)
|
||||||
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)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -360,11 +362,13 @@ describe API::Runners, api: true do
|
||||||
|
|
||||||
describe 'POST /projects/:id/runners' do
|
describe 'POST /projects/:id/runners' do
|
||||||
context 'authorized user' do
|
context 'authorized user' do
|
||||||
it 'should enable specific runner' do
|
let(:specific_runner2) do
|
||||||
specific_runner2 = create(:ci_runner).tap do |runner|
|
create(:ci_runner).tap do |runner|
|
||||||
create(:ci_runner_project, runner: runner, project: project2)
|
create(:ci_runner_project, runner: runner, project: project2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should enable specific runner' do
|
||||||
expect do
|
expect do
|
||||||
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
|
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
|
||||||
end.to change{ project.runners.count }.by(+1)
|
end.to change{ project.runners.count }.by(+1)
|
||||||
|
@ -375,7 +379,17 @@ describe API::Runners, api: true do
|
||||||
expect do
|
expect do
|
||||||
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id
|
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id
|
||||||
end.to change{ project.runners.count }.by(0)
|
end.to change{ project.runners.count }.by(0)
|
||||||
expect(response.status).to eq(201)
|
expect(response.status).to eq(409)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not enable locked runner' do
|
||||||
|
specific_runner2.update(locked: true)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
|
||||||
|
end.to change{ project.runners.count }.by(0)
|
||||||
|
|
||||||
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not enable shared runner' do
|
it 'should not enable shared runner' do
|
||||||
|
|
Loading…
Reference in New Issue