Commit b0d9c424 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'ab-move-api-runner' into 'master'

Move API::Runner and API::Runners to Ci namespace

See merge request gitlab-org/gitlab!35191
parents 6448f2b7 c50c0325
......@@ -49,7 +49,7 @@ module API
end
def process_pull_request
external_pull_request = Ci::ExternalPullRequests::ProcessGithubEventService.new(project, mirror_user).execute(params)
external_pull_request = ::Ci::ExternalPullRequests::ProcessGithubEventService.new(project, mirror_user).execute(params)
if external_pull_request
render_validation_error!(external_pull_request)
......
......@@ -131,6 +131,8 @@ module API
mount ::API::Boards
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::Ci::Runner
mount ::API::Ci::Runners
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::ContainerRegistryEvent
......@@ -195,8 +197,6 @@ module API
mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::Runner
mount ::API::Runners
mount ::API::Search
mount ::API::Services
mount ::API::Settings
......
# frozen_string_literal: true
module API
module Ci
class Runner < Grape::API
helpers ::API::Helpers::Runner
resource :runners do
desc 'Registers a new Runner' do
success Entities::RunnerRegistrationDetails
http_codes [[201, 'Runner was created'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: 'Registration token'
optional :description, type: String, desc: %q(Runner's description)
optional :info, type: Hash, desc: %q(Runner's metadata)
optional :active, type: Boolean, desc: 'Should Runner be active'
optional :locked, type: Boolean, desc: 'Should Runner be locked for current project'
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs'
optional :tag_list, type: Array[String], desc: %q(List of Runner's tags)
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
end
post '/' do
attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :access_level, :maximum_timeout])
.merge(get_runner_details_from_request)
attributes =
if runner_registration_token_valid?
# Create shared runner. Requires admin access
attributes.merge(runner_type: :instance_type)
elsif project = Project.find_by_runners_token(params[:token])
# Create a specific runner for the project
attributes.merge(runner_type: :project_type, projects: [project])
elsif group = Group.find_by_runners_token(params[:token])
# Create a specific runner for the group
attributes.merge(runner_type: :group_type, groups: [group])
else
forbidden!
end
runner = ::Ci::Runner.create(attributes)
if runner.persisted?
present runner, with: Entities::RunnerRegistrationDetails
else
render_validation_error!(runner)
end
end
desc 'Deletes a registered Runner' do
http_codes [[204, 'Runner was deleted'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runner's authentication token)
end
delete '/' do
authenticate_runner!
runner = ::Ci::Runner.find_by_token(params[:token])
destroy_conditionally!(runner)
end
desc 'Validates authentication credentials' do
http_codes [[200, 'Credentials are valid'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runner's authentication token)
end
post '/verify' do
authenticate_runner!
status 200
end
end
resource :jobs do
before do
Gitlab::ApplicationContext.push(
user: -> { current_job&.user },
project: -> { current_job&.project }
)
end
desc 'Request a job' do
success Entities::JobRequest::Response
http_codes [[201, 'Job was scheduled'],
[204, 'No job for Runner'],
[403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runner's authentication token)
optional :last_update, type: String, desc: %q(Runner's queue last_update token)
optional :info, type: Hash, desc: %q(Runner's metadata) do
optional :name, type: String, desc: %q(Runner's name)
optional :version, type: String, desc: %q(Runner's version)
optional :revision, type: String, desc: %q(Runner's revision)
optional :platform, type: String, desc: %q(Runner's platform)
optional :architecture, type: String, desc: %q(Runner's architecture)
optional :executor, type: String, desc: %q(Runner's executor)
optional :features, type: Hash, desc: %q(Runner's features)
end
optional :session, type: Hash, desc: %q(Runner's session data) do
optional :url, type: String, desc: %q(Session's url)
optional :certificate, type: String, desc: %q(Session's certificate)
optional :authorization, type: String, desc: %q(Session's authorization)
end
optional :job_age, type: Integer, desc: %q(Job should be older than passed age in seconds to be ran on runner)
end
post '/request' do
authenticate_runner!
unless current_runner.active?
header 'X-GitLab-Last-Update', current_runner.ensure_runner_queue_value
break no_content!
end
runner_params = declared_params(include_missing: false)
if current_runner.runner_queue_value_latest?(runner_params[:last_update])
header 'X-GitLab-Last-Update', runner_params[:last_update]
Gitlab::Metrics.add_event(:build_not_found_cached)
break no_content!
end
new_update = current_runner.ensure_runner_queue_value
result = ::Ci::RegisterJobService.new(current_runner).execute(runner_params)
if result.valid?
if result.build
Gitlab::Metrics.add_event(:build_found)
present ::Ci::BuildRunnerPresenter.new(result.build), with: Entities::JobRequest::Response
else
Gitlab::Metrics.add_event(:build_not_found)
header 'X-GitLab-Last-Update', new_update
no_content!
end
else
# We received build that is invalid due to concurrency conflict
Gitlab::Metrics.add_event(:build_invalid)
conflict!
end
end
desc 'Updates a job' do
http_codes [[200, 'Job was updated'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runners's authentication token)
requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed)
optional :failure_reason, type: String, desc: %q(Job's failure_reason)
end
put '/:id' do
job = authenticate_job!
job.trace.set(params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build)
case params[:state].to_s
when 'running'
job.touch if job.needs_touch?
when 'success'
job.success!
when 'failed'
job.drop!(params[:failure_reason] || :unknown_failure)
end
end
desc 'Appends a patch to the job trace' do
http_codes [[202, 'Trace was patched'],
[400, 'Missing Content-Range header'],
[403, 'Forbidden'],
[416, 'Range not satisfiable']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
end
patch '/:id/trace' do
job = authenticate_job!
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
content_range = content_range.split('-')
# TODO:
# it seems that `Content-Range` as formatted by runner is wrong,
# the `byte_end` should point to final byte, but it points byte+1
# that means that we have to calculate end of body,
# as we cannot use `content_length[1]`
# Issue: https://gitlab.com/gitlab-org/gitlab-runner/issues/3275
body_data = request.body.read
body_start = content_range[0].to_i
body_end = body_start + body_data.bytesize
stream_size = job.trace.append(body_data, body_start)
unless stream_size == body_end
break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{stream_size}" })
end
status 202
header 'Job-Status', job.status
header 'Range', "0-#{stream_size}"
header 'X-GitLab-Trace-Update-Interval', job.trace.update_interval.to_s
end
desc 'Authorize artifacts uploading for job' do
http_codes [[200, 'Upload allowed'],
[403, 'Forbidden'],
[405, 'Artifacts support not enabled'],
[413, 'File too large']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
optional :filesize, type: Integer, desc: %q(Artifacts filesize)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: ::Ci::JobArtifact.file_types.keys
end
post '/:id/artifacts/authorize' do
not_allowed! unless Gitlab.config.artifacts.enabled
require_gitlab_workhorse!
Gitlab::Workhorse.verify_api_request!(headers)
job = authenticate_job!
service = ::Ci::AuthorizeJobArtifactService.new(job, params, max_size: max_artifacts_size(job))
forbidden! if service.forbidden?
file_too_large! if service.too_large?
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
service.headers
end
desc 'Upload artifacts for job' do
success Entities::JobRequest::Response
http_codes [[201, 'Artifact uploaded'],
[400, 'Bad request'],
[403, 'Forbidden'],
[405, 'Artifacts support not enabled'],
[413, 'File too large']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact file to store (generated by Multipart middleware))
optional :token, type: String, desc: %q(Job's authentication token)
optional :expire_in, type: String, desc: %q(Specify when artifacts should expire)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: ::Ci::JobArtifact.file_types.keys
optional :artifact_format, type: String, desc: %q(The format of artifact),
default: 'zip', values: ::Ci::JobArtifact.file_formats.keys
optional :metadata, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact metadata to store (generated by Multipart middleware))
end
post '/:id/artifacts' do
not_allowed! unless Gitlab.config.artifacts.enabled
require_gitlab_workhorse!
job = authenticate_job!
artifacts = params[:file]
metadata = params[:metadata]
file_too_large! unless artifacts.size < max_artifacts_size(job)
result = ::Ci::CreateJobArtifactsService.new(job.project).execute(job, artifacts, params, metadata_file: metadata)
if result[:status] == :success
status :created
else
render_api_error!(result[:message], result[:http_status])
end
end
desc 'Download the artifacts file for job' do
http_codes [[200, 'Upload allowed'],
[403, 'Forbidden'],
[404, 'Artifact not found']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
optional :direct_download, default: false, type: Boolean, desc: %q(Perform direct download from remote storage instead of proxying artifacts)
end
get '/:id/artifacts' do
job = authenticate_job!(require_running: false)
present_carrierwave_file!(job.artifacts_file, supports_direct_download: params[:direct_download])
end
end
end
end
end
# frozen_string_literal: true
module API
module Ci
class Runners < Grape::API
include PaginationParams
before { authenticate! }
resource :runners do
desc 'Get runners available for user' do
success Entities::Runner
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
desc: 'The scope of specific runners to show'
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get do
runners = current_user.ci_owned_runners
runners = filter_runners(runners, params[:scope], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
runners = filter_runners(runners, params[:type], allowed_scopes: ::Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
desc 'Get all runners - shared and specific' do
success Entities::Runner
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show'
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get 'all' do
authenticated_as_admin!
runners = ::Ci::Runner.all
runners = filter_runners(runners, params[:scope])
runners = filter_runners(runners, params[:type], allowed_scopes: ::Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
desc "Get runner's details" do
success Entities::RunnerDetails
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
end
get ':id' do
runner = get_runner(params[:id])
authenticate_show_runner!(runner)
present runner, with: Entities::RunnerDetails, current_user: current_user
end
desc "Update runner's details" do
success Entities::RunnerDetails
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
optional :description, type: String, desc: 'The description of the runner'
optional :active, type: Boolean, desc: 'The state of a runner'
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'
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
end
put ':id' do
runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
update_service = ::Ci::UpdateRunnerService.new(runner)
if update_service.update(declared_params(include_missing: false))
present runner, with: Entities::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
end
end
desc 'Remove a runner' do
success Entities::Runner
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
end
delete ':id' do
runner = get_runner(params[:id])
authenticate_delete_runner!(runner)
destroy_conditionally!(runner)
end
desc 'List jobs running on a runner' do
success Entities::JobBasicWithProject
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
optional :order_by, type: String, desc: 'Order by `id` or not', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
get ':id/jobs' do
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
jobs = ::Ci::RunnerJobsFinder.new(runner, params).execute
present paginate(jobs), with: Entities::JobBasicWithProject
end
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before { authorize_admin_project }
desc 'Get runners available for project' do
success Entities::Runner
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show'
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
runners = ::Ci::Runner.owned_or_instance_wide(user_project.id)
# scope is deprecated (for project runners), however api documentation still supports it.
# Not including them in `apply_filter` method as it's not supported for group runners
runners = filter_runners(runners, params[:scope])
runners = apply_filter(runners, params)
present paginate(runners), with: Entities::Runner
end
desc 'Enable a runner for a project' do
success Entities::Runner
end
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
end
post ':id/runners' do
runner = get_runner(params[:runner_id])
authenticate_enable_runner!(runner)
if runner.assign_to(user_project)
present runner, with: Entities::Runner
else
render_validation_error!(runner)
end
end
desc "Disable project's runner" do
success Entities::Runner
end
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
end
# rubocop: disable CodeReuse/ActiveRecord
delete ':id/runners/:runner_id' do
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
not_found!('Runner') unless runner_project
runner = runner_project.runner
forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
destroy_conditionally!(runner_project)
end
# rubocop: enable CodeReuse/ActiveRecord
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before { authorize_admin_group }
desc 'Get runners available for group' do
success Entities::Runner
end
params do
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
runners = ::Ci::Runner.belonging_to_group(user_group.id, include_ancestors: true)
runners = apply_filter(runners, params)
present paginate(runners), with: Entities::Runner
end
end
helpers do
def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES)
return runners unless scope.present?
unless allowed_scopes.include?(scope)
render_api_error!('Scope contains invalid value', 400)
end
# Support deprecated scopes
if runners.respond_to?("deprecated_#{scope}")
scope = "deprecated_#{scope}"
end
runners.public_send(scope) # rubocop:disable GitlabSecurity/PublicSend
end
def apply_filter(runners, params)
runners = filter_runners(runners, params[:type], allowed_scopes: ::Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
runners
end
def get_runner(id)
runner = ::Ci::Runner.find(id)
not_found!('Runner') unless runner
runner
end
def authenticate_show_runner!(runner)
return if runner.instance_type? || current_user.admin?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
def authenticate_update_runner!(runner)
return if current_user.admin?
forbidden!("No access granted") unless can?(current_user, :update_runner, runner)
end
def authenticate_delete_runner!(runner)
return if current_user.admin?
forbidden!("Runner associated with more than one project") if runner.projects.count > 1
forbidden!("No access granted") unless can?(current_user, :delete_runner, runner)
end
def authenticate_enable_runner!(runner)
forbidden!("Runner is a group runner") if runner.group_type?
return if current_user.admin?
forbidden!("Runner is locked") if runner.locked?
forbidden!("No access granted") unless can?(current_user, :assign_runner, runner)
end
def authenticate_list_runners_jobs!(runner)
return if current_user.admin?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
end
end
end
end
......@@ -60,7 +60,7 @@ module API
not_found! 'Commit' unless commit
# Since the CommitStatus is attached to Ci::Pipeline (in the future Pipeline)
# Since the CommitStatus is attached to ::Ci::Pipeline (in the future Pipeline)
# We need to always have the pipeline object
# To have a valid pipeline object that can be attached to specific MR
# Other CI service needs to send `ref`
......
......@@ -48,7 +48,7 @@ module API
requires :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
optional :masked, type: String, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
optional :variable_type, type: String, values: ::Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
end
post ':id/variables' do
variable_params = declared_params(include_missing: false)
......@@ -70,7 +70,7 @@ module API
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
optional :masked, type: String, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
optional :variable_type, type: String, values: ::Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/variables/:key' do
......
......@@ -60,7 +60,7 @@ module API
def current_job
strong_memoize(:current_job) do
Ci::Build.find_by_id(params[:id])
::Ci::Build.find_by_id(params[:id])
end
end
......
......@@ -160,7 +160,7 @@ module API
authorize!(:update_build, build)
break forbidden!('Job is not retryable') unless build.retryable?
build = Ci::Build.retry(build, current_user)
build = ::Ci::Build.retry(build, current_user)
present build, with: Entities::Job
end
......
......@@ -22,7 +22,7 @@ module API
get ':id/pipeline_schedules' do
authorize! :read_pipeline_schedule, user_project
schedules = Ci::PipelineSchedulesFinder.new(user_project).execute(scope: params[:scope])
schedules = ::Ci::PipelineSchedulesFinder.new(user_project).execute(scope: params[:scope])
.preload([:owner, :last_pipeline])
present paginate(schedules), with: Entities::PipelineSchedule
end
......@@ -51,7 +51,7 @@ module API
post ':id/pipeline_schedules' do
authorize! :create_pipeline_schedule, user_project
pipeline_schedule = Ci::CreatePipelineScheduleService
pipeline_schedule = ::Ci::CreatePipelineScheduleService
.new(user_project, current_user, declared_params(include_missing: false))
.execute
......@@ -137,7 +137,7 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable'
optional :variable_type, type: String, values: Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
optional :variable_type, type: String, values: ::Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
end
post ':id/pipeline_schedules/:pipeline_schedule_id/variables' do
authorize! :update_pipeline_schedule, pipeline_schedule
......@@ -158,7 +158,7 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
requires :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable'
optional :variable_type, type: String, values: Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
optional :variable_type, type: String, values: ::Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
end
put ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
authorize! :update_pipeline_schedule, pipeline_schedule
......
......@@ -18,7 +18,7 @@ module API
use :pagination
optional :scope, type: String, values: %w[running pending finished branches tags],
desc: 'The scope of pipelines'
optional :status, type: String, values: Ci::HasStatus::AVAILABLE_STATUSES,
optional :status, type: String, values: ::Ci::HasStatus::AVAILABLE_STATUSES,
desc: 'The status of pipelines'
optional :ref, type: String, desc: 'The ref of pipelines'
optional :sha, type: String, desc: 'The sha of pipelines'
......@@ -27,7 +27,7 @@ module API
optional :username, type: String, desc: 'The username of the user who triggered pipelines'
optional :updated_before, type: DateTime, desc: 'Return pipelines updated before the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
optional :updated_after, type: DateTime, desc: 'Return pipelines updated after the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
optional :order_by, type: String, values: Ci::PipelinesFinder::ALLOWED_INDEXED_COLUMNS, default: 'id',
optional :order_by, type: String, values: ::Ci::PipelinesFinder::ALLOWED_INDEXED_COLUMNS, default: 'id',
desc: 'Order pipelines'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Sort pipelines'
......@@ -36,7 +36,7 @@ module API
authorize! :read_pipeline, user_project
authorize! :read_build, user_project
pipelines = Ci::PipelinesFinder.new(user_project, current_user, params).execute
pipelines = ::Ci::PipelinesFinder.new(user_project, current_user, params).execute
present paginate(pipelines), with: Entities::PipelineBasic
end
......@@ -57,7 +57,7 @@ module API
.merge(variables_attributes: params[:variables])
.except(:variables)
new_pipeline = Ci::CreatePipelineService.new(user_project,
new_pipeline = ::Ci::CreatePipelineService.new(user_project,
current_user,
pipeline_params)
.execute(:api, ignore_skip_ci: true, save_on_errors: false)
......
# frozen_string_literal: true
module API
class Runner < Grape::API
helpers ::API::Helpers::Runner
resource :runners do
desc 'Registers a new Runner' do
success Entities::RunnerRegistrationDetails
http_codes [[201, 'Runner was created'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: 'Registration token'
optional :description, type: String, desc: %q(Runner's description)
optional :info, type: Hash, desc: %q(Runner's metadata)
optional :active, type: Boolean, desc: 'Should Runner be active'
optional :locked, type: Boolean, desc: 'Should Runner be locked for current project'
optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs'
optional :tag_list, type: Array[String], desc: %q(List of Runner's tags)
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
end
post '/' do
attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :access_level, :maximum_timeout])
.merge(get_runner_details_from_request)
attributes =
if runner_registration_token_valid?
# Create shared runner. Requires admin access
attributes.merge(runner_type: :instance_type)
elsif project = Project.find_by_runners_token(params[:token])
# Create a specific runner for the project
attributes.merge(runner_type: :project_type, projects: [project])
elsif group = Group.find_by_runners_token(params[:token])
# Create a specific runner for the group
attributes.merge(runner_type: :group_type, groups: [group])
else
forbidden!
end
runner = Ci::Runner.create(attributes)
if runner.persisted?
present runner, with: Entities::RunnerRegistrationDetails
else
render_validation_error!(runner)
end
end
desc 'Deletes a registered Runner' do
http_codes [[204, 'Runner was deleted'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runner's authentication token)
end
delete '/' do
authenticate_runner!
runner = Ci::Runner.find_by_token(params[:token])
destroy_conditionally!(runner)
end
desc 'Validates authentication credentials' do
http_codes [[200, 'Credentials are valid'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runner's authentication token)
end
post '/verify' do
authenticate_runner!
status 200
end
end
resource :jobs do
before do
Gitlab::ApplicationContext.push(
user: -> { current_job&.user },
project: -> { current_job&.project }
)
end
desc 'Request a job' do
success Entities::JobRequest::Response
http_codes [[201, 'Job was scheduled'],
[204, 'No job for Runner'],
[403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runner's authentication token)
optional :last_update, type: String, desc: %q(Runner's queue last_update token)
optional :info, type: Hash, desc: %q(Runner's metadata) do
optional :name, type: String, desc: %q(Runner's name)
optional :version, type: String, desc: %q(Runner's version)
optional :revision, type: String, desc: %q(Runner's revision)
optional :platform, type: String, desc: %q(Runner's platform)
optional :architecture, type: String, desc: %q(Runner's architecture)
optional :executor, type: String, desc: %q(Runner's executor)
optional :features, type: Hash, desc: %q(Runner's features)
end
optional :session, type: Hash, desc: %q(Runner's session data) do
optional :url, type: String, desc: %q(Session's url)
optional :certificate, type: String, desc: %q(Session's certificate)
optional :authorization, type: String, desc: %q(Session's authorization)
end
optional :job_age, type: Integer, desc: %q(Job should be older than passed age in seconds to be ran on runner)
end
post '/request' do
authenticate_runner!
unless current_runner.active?
header 'X-GitLab-Last-Update', current_runner.ensure_runner_queue_value
break no_content!
end
runner_params = declared_params(include_missing: false)
if current_runner.runner_queue_value_latest?(runner_params[:last_update])
header 'X-GitLab-Last-Update', runner_params[:last_update]
Gitlab::Metrics.add_event(:build_not_found_cached)
break no_content!
end
new_update = current_runner.ensure_runner_queue_value
result = ::Ci::RegisterJobService.new(current_runner).execute(runner_params)
if result.valid?
if result.build
Gitlab::Metrics.add_event(:build_found)
present Ci::BuildRunnerPresenter.new(result.build), with: Entities::JobRequest::Response
else
Gitlab::Metrics.add_event(:build_not_found)
header 'X-GitLab-Last-Update', new_update
no_content!
end
else
# We received build that is invalid due to concurrency conflict
Gitlab::Metrics.add_event(:build_invalid)
conflict!
end
end
desc 'Updates a job' do
http_codes [[200, 'Job was updated'], [403, 'Forbidden']]
end
params do
requires :token, type: String, desc: %q(Runners's authentication token)
requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed)
optional :failure_reason, type: String, desc: %q(Job's failure_reason)
end
put '/:id' do
job = authenticate_job!
job.trace.set(params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build)
case params[:state].to_s
when 'running'
job.touch if job.needs_touch?
when 'success'
job.success!
when 'failed'
job.drop!(params[:failure_reason] || :unknown_failure)
end
end
desc 'Appends a patch to the job trace' do
http_codes [[202, 'Trace was patched'],
[400, 'Missing Content-Range header'],
[403, 'Forbidden'],
[416, 'Range not satisfiable']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
end
patch '/:id/trace' do
job = authenticate_job!
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
content_range = content_range.split('-')
# TODO:
# it seems that `Content-Range` as formatted by runner is wrong,
# the `byte_end` should point to final byte, but it points byte+1
# that means that we have to calculate end of body,
# as we cannot use `content_length[1]`
# Issue: https://gitlab.com/gitlab-org/gitlab-runner/issues/3275
body_data = request.body.read
body_start = content_range[0].to_i
body_end = body_start + body_data.bytesize
stream_size = job.trace.append(body_data, body_start)
unless stream_size == body_end
break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{stream_size}" })
end
status 202
header 'Job-Status', job.status
header 'Range', "0-#{stream_size}"
header 'X-GitLab-Trace-Update-Interval', job.trace.update_interval.to_s
end
desc 'Authorize artifacts uploading for job' do
http_codes [[200, 'Upload allowed'],
[403, 'Forbidden'],
[405, 'Artifacts support not enabled'],
[413, 'File too large']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
optional :filesize, type: Integer, desc: %q(Artifacts filesize)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: Ci::JobArtifact.file_types.keys
end
post '/:id/artifacts/authorize' do
not_allowed! unless Gitlab.config.artifacts.enabled
require_gitlab_workhorse!
Gitlab::Workhorse.verify_api_request!(headers)
job = authenticate_job!
service = Ci::AuthorizeJobArtifactService.new(job, params, max_size: max_artifacts_size(job))
forbidden! if service.forbidden?
file_too_large! if service.too_large?
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
service.headers
end
desc 'Upload artifacts for job' do
success Entities::JobRequest::Response
http_codes [[201, 'Artifact uploaded'],
[400, 'Bad request'],
[403, 'Forbidden'],
[405, 'Artifacts support not enabled'],
[413, 'File too large']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact file to store (generated by Multipart middleware))
optional :token, type: String, desc: %q(Job's authentication token)
optional :expire_in, type: String, desc: %q(Specify when artifacts should expire)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: Ci::JobArtifact.file_types.keys
optional :artifact_format, type: String, desc: %q(The format of artifact),
default: 'zip', values: Ci::JobArtifact.file_formats.keys
optional :metadata, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact metadata to store (generated by Multipart middleware))
end
post '/:id/artifacts' do
not_allowed! unless Gitlab.config.artifacts.enabled
require_gitlab_workhorse!
job = authenticate_job!
artifacts = params[:file]
metadata = params[:metadata]
file_too_large! unless artifacts.size < max_artifacts_size(job)
result = Ci::CreateJobArtifactsService.new(job.project).execute(job, artifacts, params, metadata_file: metadata)
if result[:status] == :success
status :created
else
render_api_error!(result[:message], result[:http_status])
end
end
desc 'Download the artifacts file for job' do
http_codes [[200, 'Upload allowed'],
[403, 'Forbidden'],
[404, 'Artifact not found']]
end
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
optional :direct_download, default: false, type: Boolean, desc: %q(Perform direct download from remote storage instead of proxying artifacts)
end
get '/:id/artifacts' do
job = authenticate_job!(require_running: false)
present_carrierwave_file!(job.artifacts_file, supports_direct_download: params[:direct_download])
end
end
end
end
# frozen_string_literal: true
module API
class Runners < Grape::API
include PaginationParams
before { authenticate! }
resource :runners do
desc 'Get runners available for user' do
success Entities::Runner
end
params do
optional :scope, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The scope of specific runners to show'
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get do
runners = current_user.ci_owned_runners
runners = filter_runners(runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
desc 'Get all runners - shared and specific' do
success Entities::Runner
end
params do
optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show'
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get 'all' do
authenticated_as_admin!
runners = Ci::Runner.all
runners = filter_runners(runners, params[:scope])
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
desc "Get runner's details" do
success Entities::RunnerDetails
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
end
get ':id' do
runner = get_runner(params[:id])
authenticate_show_runner!(runner)
present runner, with: Entities::RunnerDetails, current_user: current_user
end
desc "Update runner's details" do
success Entities::RunnerDetails
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
optional :description, type: String, desc: 'The description of the runner'
optional :active, type: Boolean, desc: 'The state of a runner'
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'
optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
end
put ':id' do
runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
update_service = Ci::UpdateRunnerService.new(runner)
if update_service.update(declared_params(include_missing: false))
present runner, with: Entities::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
end
end
desc 'Remove a runner' do
success Entities::Runner
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
end
delete ':id' do
runner = get_runner(params[:id])
authenticate_delete_runner!(runner)
destroy_conditionally!(runner)
end
desc 'List jobs running on a runner' do
success Entities::JobBasicWithProject
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
optional :status, type: String, desc: 'Status of the job', values: Ci::Build::AVAILABLE_STATUSES
optional :order_by, type: String, desc: 'Order by `id` or not', values: Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
get ':id/jobs' do
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
jobs = Ci::RunnerJobsFinder.new(runner, params).execute
present paginate(jobs), with: Entities::JobBasicWithProject
end
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before { authorize_admin_project }
desc 'Get runners available for project' do
success Entities::Runner
end
params do
optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show'
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
runners = Ci::Runner.owned_or_instance_wide(user_project.id)
# scope is deprecated (for project runners), however api documentation still supports it.
# Not including them in `apply_filter` method as it's not supported for group runners
runners = filter_runners(runners, params[:scope])
runners = apply_filter(runners, params)
present paginate(runners), with: Entities::Runner
end
desc 'Enable a runner for a project' do
success Entities::Runner
end
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
end
post ':id/runners' do
runner = get_runner(params[:runner_id])
authenticate_enable_runner!(runner)
if runner.assign_to(user_project)
present runner, with: Entities::Runner
else
render_validation_error!(runner)
end
end
desc "Disable project's runner" do
success Entities::Runner
end
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
end
# rubocop: disable CodeReuse/ActiveRecord
delete ':id/runners/:runner_id' do
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
not_found!('Runner') unless runner_project
runner = runner_project.runner
forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
destroy_conditionally!(runner_project)
end
# rubocop: enable CodeReuse/ActiveRecord
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before { authorize_admin_group }
desc 'Get runners available for group' do
success Entities::Runner
end
params do
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
runners = Ci::Runner.belonging_to_group(user_group.id, include_ancestors: true)
runners = apply_filter(runners, params)
present paginate(runners), with: Entities::Runner
end
end
helpers do
def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES)
return runners unless scope.present?
unless allowed_scopes.include?(scope)
render_api_error!('Scope contains invalid value', 400)
end
# Support deprecated scopes
if runners.respond_to?("deprecated_#{scope}")
scope = "deprecated_#{scope}"
end
runners.public_send(scope) # rubocop:disable GitlabSecurity/PublicSend
end
def apply_filter(runners, params)
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
runners
end
def get_runner(id)
runner = Ci::Runner.find(id)
not_found!('Runner') unless runner
runner
end
def authenticate_show_runner!(runner)
return if runner.instance_type? || current_user.admin?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
def authenticate_update_runner!(runner)
return if current_user.admin?
forbidden!("No access granted") unless can?(current_user, :update_runner, runner)
end
def authenticate_delete_runner!(runner)
return if current_user.admin?
forbidden!("Runner associated with more than one project") if runner.projects.count > 1
forbidden!("No access granted") unless can?(current_user, :delete_runner, runner)
end
def authenticate_enable_runner!(runner)
forbidden!("Runner is a group runner") if runner.group_type?
return if current_user.admin?
forbidden!("Runner is locked") if runner.locked?
forbidden!("No access granted") unless can?(current_user, :assign_runner, runner)
end
def authenticate_list_runners_jobs!(runner)
return if current_user.admin?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
end
end
end
......@@ -32,7 +32,7 @@ module API
project = find_project(params[:id])
not_found! unless project
result = Ci::PipelineTriggerService.new(project, nil, params).execute
result = ::Ci::PipelineTriggerService.new(project, nil, params).execute
not_found! unless result
if result[:http_status]
......
......@@ -56,7 +56,7 @@ module API
requires :value, type: String, desc: 'The value of the variable'
optional :protected, type: Boolean, desc: 'Whether the variable is protected'
optional :masked, type: Boolean, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
end
post ':id/variables' do
......@@ -80,7 +80,7 @@ module API
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: Boolean, desc: 'Whether the variable is protected'
optional :masked, type: Boolean, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
end
# rubocop: disable CodeReuse/ActiveRecord
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
......@@ -13,7 +13,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
stub_feature_flags(ci_enable_live_trace: true)
stub_gitlab_calls
stub_application_setting(runners_registration_token: registration_token)
allow_any_instance_of(Ci::Runner).to receive(:cache_attributes)
allow_any_instance_of(::Ci::Runner).to receive(:cache_attributes)
end
describe '/api/v4/runners' do
......@@ -38,7 +38,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
it 'creates runner with default values' do
post api('/runners'), params: { token: registration_token }
runner = Ci::Runner.first
runner = ::Ci::Runner.first
expect(response).to have_gitlab_http_status(:created)
expect(json_response['id']).to eq(runner.id)
......@@ -57,7 +57,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:created)
expect(project.runners.size).to eq(1)
runner = Ci::Runner.first
runner = ::Ci::Runner.first
expect(runner.token).not_to eq(registration_token)
expect(runner.token).not_to eq(project.runners_token)
expect(runner).to be_project_type
......@@ -72,7 +72,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:created)
expect(group.runners.reload.size).to eq(1)
runner = Ci::Runner.first
runner = ::Ci::Runner.first
expect(runner.token).not_to eq(registration_token)
expect(runner.token).not_to eq(group.runners_token)
expect(runner).to be_group_type
......@@ -88,7 +88,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.description).to eq('server.hostname')
expect(::Ci::Runner.first.description).to eq('server.hostname')
end
end
......@@ -100,7 +100,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.tag_list.sort).to eq(%w(tag1 tag2))
expect(::Ci::Runner.first.tag_list.sort).to eq(%w(tag1 tag2))
end
end
......@@ -114,8 +114,8 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.run_untagged).to be false
expect(Ci::Runner.first.tag_list.sort).to eq(['tag'])
expect(::Ci::Runner.first.run_untagged).to be false
expect(::Ci::Runner.first.tag_list.sort).to eq(['tag'])
end
end
......@@ -141,7 +141,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.locked).to be true
expect(::Ci::Runner.first.locked).to be true
end
end
......@@ -154,7 +154,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.active).to be true
expect(::Ci::Runner.first.active).to be true
end
end
......@@ -166,7 +166,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.active).to be false
expect(::Ci::Runner.first.active).to be false
end
end
end
......@@ -180,7 +180,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.ref_protected?).to be true
expect(::Ci::Runner.first.ref_protected?).to be true
end
end
......@@ -192,7 +192,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.ref_protected?).to be false
expect(::Ci::Runner.first.ref_protected?).to be false
end
end
end
......@@ -205,7 +205,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.maximum_timeout).to eq(9000)
expect(::Ci::Runner.first.maximum_timeout).to eq(9000)
end
context 'when maximum job timeout is empty' do
......@@ -216,7 +216,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.maximum_timeout).to be_nil
expect(::Ci::Runner.first.maximum_timeout).to be_nil
end
end
end
......@@ -232,7 +232,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
}
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.read_attribute(param.to_sym)).to eq(value)
expect(::Ci::Runner.first.read_attribute(param.to_sym)).to eq(value)
end
end
end
......@@ -243,7 +243,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
headers: { 'X-Forwarded-For' => '123.111.123.111' }
expect(response).to have_gitlab_http_status(:created)
expect(Ci::Runner.first.ip_address).to eq('123.111.123.111')
expect(::Ci::Runner.first.ip_address).to eq('123.111.123.111')
end
end
......@@ -271,7 +271,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
delete api('/runners'), params: { token: runner.token }
expect(response).to have_gitlab_http_status(:no_content)
expect(Ci::Runner.count).to eq(0)
expect(::Ci::Runner.count).to eq(0)
end
it_behaves_like '412 response' do
......@@ -537,7 +537,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
end
it 'creates persistent ref' do
expect_any_instance_of(Ci::PersistentRef).to receive(:create_ref)
expect_any_instance_of(::Ci::PersistentRef).to receive(:create_ref)
.with(job.sha, "refs/#{Repository::REF_PIPELINES}/#{job.commit_id}")
request_job info: { platform: :darwin }
......@@ -749,7 +749,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
context 'when concurrently updating a job' do
before do
expect_any_instance_of(Ci::Build).to receive(:run!)
expect_any_instance_of(::Ci::Build).to receive(:run!)
.and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
end
......@@ -890,7 +890,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
let!(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, builds: [job], trigger: trigger) }
before do
project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
project.variables << ::Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
end
shared_examples 'expected variables behavior' do
......@@ -1099,7 +1099,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
let_it_be(:project) { create(:project, :repository) }
let(:runner) { create(:ci_runner, :project, projects: [project]) }
let(:service) { Ci::CreateWebIdeTerminalService.new(project, user, ref: 'master').execute }
let(:service) { ::Ci::CreateWebIdeTerminalService.new(project, user, ref: 'master').execute }
let(:pipeline) { service[:pipeline] }
let(:build) { pipeline.builds.first }
let(:job) { {} }
......@@ -2258,7 +2258,7 @@ RSpec.describe API::Runner, :clean_gitlab_redis_shared_state do
FileUtils.remove_entry(new_tmpdir)
end
it' "fails to post artifacts for outside of tmp path"' do
it 'fails to post artifacts for outside of tmp path' do
upload_artifacts(file_upload, headers_with_token)
expect(response).to have_gitlab_http_status(:bad_request)
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe API::Runners do
RSpec.describe API::Ci::Runners do
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
......@@ -266,7 +266,7 @@ RSpec.describe API::Runners do
delete api("/runners/#{unused_project_runner.id}", admin)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.project_type.count }.by(-1)
end.to change { ::Ci::Runner.project_type.count }.by(-1)
end
end
......@@ -493,7 +493,7 @@ RSpec.describe API::Runners do
delete api("/runners/#{shared_runner.id}", admin)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.instance_type.count }.by(-1)
end.to change { ::Ci::Runner.instance_type.count }.by(-1)
end
it_behaves_like '412 response' do
......@@ -507,7 +507,7 @@ RSpec.describe API::Runners do
delete api("/runners/#{project_runner.id}", admin)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.project_type.count }.by(-1)
end.to change { ::Ci::Runner.project_type.count }.by(-1)
end
end
......@@ -542,7 +542,7 @@ RSpec.describe API::Runners do
delete api("/runners/#{project_runner.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.project_type.count }.by(-1)
end.to change { ::Ci::Runner.project_type.count }.by(-1)
end
it 'does not delete group runner with guest access' do
......@@ -574,7 +574,7 @@ RSpec.describe API::Runners do
delete api("/runners/#{group_runner_a.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.group_type.count }.by(-1)
end.to change { ::Ci::Runner.group_type.count }.by(-1)
end
it 'deletes inherited group runner with owner access' do
......@@ -582,7 +582,7 @@ RSpec.describe API::Runners do
delete api("/runners/#{group_runner_b.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.group_type.count }.by(-1)
end.to change { ::Ci::Runner.group_type.count }.by(-1)
end
it_behaves_like '412 response' do
......@@ -968,7 +968,7 @@ RSpec.describe API::Runners do
end
it 'does not enable locked runner' do
project_runner2.update(locked: true)
project_runner2.update!(locked: true)
expect do
post api("/projects/#{project.id}/runners", user), params: { runner_id: project_runner2.id }
......
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