diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-21 00:09:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-21 00:09:52 +0300 |
commit | 900c5cc840cccdce182aef5d5050a7950de9ad00 (patch) | |
tree | 08bf4419b68ce3e41abde6bc793da8c2c4527b78 | |
parent | 1bf106b17275c87cf8baa199599f113f154a52fe (diff) |
Add latest changes from gitlab-org/gitlab@master
27 files changed, 794 insertions, 784 deletions
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index 3785023c9af..993ccb33655 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -3,7 +3,7 @@ module Clusters module Applications class Runner < ApplicationRecord - VERSION = '0.30.0' + VERSION = '0.31.0' self.table_name = 'clusters_applications_runners' diff --git a/doc/api/packages/helm.md b/doc/api/packages/helm.md index a76fa9d3755..f1d5f24cd99 100644 --- a/doc/api/packages/helm.md +++ b/doc/api/packages/helm.md @@ -11,8 +11,7 @@ This is the API documentation for [Helm](../../user/packages/helm_repository/ind WARNING: This API is used by the Helm-related package clients such as [Helm](https://helm.sh/) and [`helm-push`](https://github.com/chartmuseum/helm-push/#readme), -and is generally not meant for manual consumption. This API is under development and is not ready -for production use due to limited functionality. +and is generally not meant for manual consumption. For instructions on how to upload and install Helm packages from the GitLab Package Registry, see the [Helm registry documentation](../../user/packages/helm_repository/index.md). diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md index fb00fe748d0..fc4180bede7 100644 --- a/doc/development/ee_features.md +++ b/doc/development/ee_features.md @@ -673,17 +673,19 @@ class definition to make it easy and clear: ```ruby module API - class JobArtifacts < Grape::API::Instance - # EE::API::JobArtifacts would override the following helpers - helpers do - def authorize_download_artifacts! - authorize_read_builds! + module Ci + class JobArtifacts < Grape::API::Instance + # EE::API::Ci::JobArtifacts would override the following helpers + helpers do + def authorize_download_artifacts! + authorize_read_builds! + end end end end end -API::JobArtifacts.prepend_mod_with('API::JobArtifacts') +API::Ci::JobArtifacts.prepend_mod_with('API::Ci::JobArtifacts') ``` And then we can follow regular object-oriented practices to override it: @@ -691,14 +693,16 @@ And then we can follow regular object-oriented practices to override it: ```ruby module EE module API - module JobArtifacts - extend ActiveSupport::Concern + module Ci + module JobArtifacts + extend ActiveSupport::Concern - prepended do - helpers do - def authorize_download_artifacts! - super - check_cross_project_pipelines_feature! + prepended do + helpers do + def authorize_download_artifacts! + super + check_cross_project_pipelines_feature! + end end end end diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index 4a7e178072b..2522e69b4d1 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -15,6 +15,8 @@ GitLab allows [Owners](../user/permissions.md) to set a project's visibility as: These visibility levels affect who can see the project in the public access directory (`/public` for your GitLab instance). For example, <https://gitlab.com/public>. +You can control the visibility of individual features with +[project feature settings](../user/permissions.md#project-features). ## Public projects diff --git a/doc/user/packages/helm_repository/index.md b/doc/user/packages/helm_repository/index.md index 26d8bf76cd6..fcd1aa04ee3 100644 --- a/doc/user/packages/helm_repository/index.md +++ b/doc/user/packages/helm_repository/index.md @@ -8,10 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18997) in GitLab 14.1. -WARNING: -The Helm package registry for GitLab is under development and isn't ready for production use due to -limited functionality. - Publish Helm packages in your project's Package Registry. Then install the packages whenever you need to use them as a dependency. diff --git a/lib/api/api.rb b/lib/api/api.rb index f9e89191a36..0a8a9682f35 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -153,10 +153,14 @@ module API mount ::API::Branches mount ::API::BroadcastMessages mount ::API::BulkImports + mount ::API::Ci::JobArtifacts + mount ::API::Ci::Jobs mount ::API::Ci::Pipelines mount ::API::Ci::PipelineSchedules mount ::API::Ci::Runner mount ::API::Ci::Runners + mount ::API::Ci::Triggers + mount ::API::Ci::Variables mount ::API::Commits mount ::API::CommitStatuses mount ::API::ContainerRegistryEvent @@ -190,8 +194,6 @@ module API mount ::API::IssueLinks mount ::API::Invitations mount ::API::Issues - mount ::API::JobArtifacts - mount ::API::Jobs mount ::API::Keys mount ::API::Labels mount ::API::Lint @@ -268,14 +270,12 @@ module API mount ::API::Tags mount ::API::Templates mount ::API::Todos - mount ::API::Triggers mount ::API::Unleash mount ::API::UsageData mount ::API::UsageDataQueries mount ::API::UsageDataNonSqlMetrics mount ::API::UserCounts mount ::API::Users - mount ::API::Variables mount ::API::Version mount ::API::Wikis end diff --git a/lib/api/ci/helpers/runner.rb b/lib/api/ci/helpers/runner.rb new file mode 100644 index 00000000000..b9662b822fb --- /dev/null +++ b/lib/api/ci/helpers/runner.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +module API + module Ci + module Helpers + module Runner + include Gitlab::Utils::StrongMemoize + + prepend_mod_with('API::Ci::Helpers::Runner') # rubocop: disable Cop/InjectEnterpriseEditionModule + + JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN' + JOB_TOKEN_PARAM = :token + + def runner_registration_token_valid? + ActiveSupport::SecurityUtils.secure_compare(params[:token], Gitlab::CurrentSettings.runners_registration_token) + end + + def runner_registrar_valid?(type) + Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?(type) + end + + def authenticate_runner! + forbidden! unless current_runner + + current_runner + .heartbeat(get_runner_details_from_request) + end + + def get_runner_details_from_request + return get_runner_ip unless params['info'].present? + + attributes_for_keys(%w(name version revision platform architecture), params['info']) + .merge(get_runner_config_from_request) + .merge(get_runner_ip) + end + + def get_runner_ip + { ip_address: ip_address } + end + + def current_runner + token = params[:token] + + if token + ::Gitlab::Database::LoadBalancing::RackMiddleware + .stick_or_unstick(env, :runner, token) + end + + strong_memoize(:current_runner) do + ::Ci::Runner.find_by_token(token.to_s) + end + end + + # HTTP status codes to terminate the job on GitLab Runner: + # - 403 + def authenticate_job!(require_running: true) + job = current_job + + # 404 is not returned here because we want to terminate the job if it's + # running. A 404 can be returned from anywhere in the networking stack which is why + # we are explicit about a 403, we should improve this in + # https://gitlab.com/gitlab-org/gitlab/-/issues/327703 + forbidden! unless job + + forbidden! unless job_token_valid?(job) + + forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete? + forbidden!('Job has been erased!') if job.erased? + + if require_running + job_forbidden!(job, 'Job is not running') unless job.running? + end + + job.runner&.heartbeat(get_runner_ip) + + job + end + + def current_job + id = params[:id] + + if id + ::Gitlab::Database::LoadBalancing::RackMiddleware + .stick_or_unstick(env, :build, id) + end + + strong_memoize(:current_job) do + ::Ci::Build.find_by_id(id) + end + end + + def job_token_valid?(job) + token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s + token && job.valid_token?(token) + end + + def job_forbidden!(job, reason) + header 'Job-Status', job.status + forbidden!(reason) + end + + def set_application_context + return unless current_job + + Gitlab::ApplicationContext.push( + user: -> { current_job.user }, + project: -> { current_job.project } + ) + end + + def track_ci_minutes_usage!(_build, _runner) + # noop: overridden in EE + end + + private + + def get_runner_config_from_request + { config: attributes_for_keys(%w(gpus), params.dig('info', 'config')) } + end + end + end + end +end diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb new file mode 100644 index 00000000000..6431436b50d --- /dev/null +++ b/lib/api/ci/job_artifacts.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +module API + module Ci + class JobArtifacts < ::API::Base + before { authenticate_non_get! } + + feature_category :build_artifacts + + # EE::API::Ci::JobArtifacts would override the following helpers + helpers do + def authorize_download_artifacts! + authorize_read_builds! + end + end + + prepend_mod_with('API::Ci::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + desc 'Download the artifacts archive from a job' do + detail 'This feature was introduced in GitLab 8.10' + end + params do + requires :ref_name, type: String, desc: 'The ref from repository' + requires :job, type: String, desc: 'The name for the job' + end + route_setting :authentication, job_token_allowed: true + get ':id/jobs/artifacts/:ref_name/download', + requirements: { ref_name: /.+/ } do + authorize_download_artifacts! + + latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) + authorize_read_job_artifacts!(latest_build) + + present_carrierwave_file!(latest_build.artifacts_file) + end + + desc 'Download a specific file from artifacts archive from a ref' do + detail 'This feature was introduced in GitLab 11.5' + end + params do + requires :ref_name, type: String, desc: 'The ref from repository' + requires :job, type: String, desc: 'The name for the job' + requires :artifact_path, type: String, desc: 'Artifact path' + end + route_setting :authentication, job_token_allowed: true + get ':id/jobs/artifacts/:ref_name/raw/*artifact_path', + format: false, + requirements: { ref_name: /.+/ } do + authorize_download_artifacts! + + build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) + authorize_read_job_artifacts!(build) + + path = Gitlab::Ci::Build::Artifacts::Path + .new(params[:artifact_path]) + + bad_request! unless path.valid? + + send_artifacts_entry(build.artifacts_file, path) + end + + desc 'Download the artifacts archive from a job' do + detail 'This feature was introduced in GitLab 8.5' + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + route_setting :authentication, job_token_allowed: true + get ':id/jobs/:job_id/artifacts' do + authorize_download_artifacts! + + build = find_build!(params[:job_id]) + authorize_read_job_artifacts!(build) + + present_carrierwave_file!(build.artifacts_file) + end + + desc 'Download a specific file from artifacts archive' do + detail 'This feature was introduced in GitLab 10.0' + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + requires :artifact_path, type: String, desc: 'Artifact path' + end + route_setting :authentication, job_token_allowed: true + get ':id/jobs/:job_id/artifacts/*artifact_path', format: false do + authorize_download_artifacts! + + build = find_build!(params[:job_id]) + authorize_read_job_artifacts!(build) + + not_found! unless build.available_artifacts? + + path = Gitlab::Ci::Build::Artifacts::Path + .new(params[:artifact_path]) + + bad_request! unless path.valid? + + send_artifacts_entry(build.artifacts_file, path) + end + + desc 'Keep the artifacts to prevent them from being deleted' do + success ::API::Entities::Ci::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + post ':id/jobs/:job_id/artifacts/keep' do + authorize_update_builds! + + build = find_build!(params[:job_id]) + authorize!(:update_build, build) + break not_found!(build) unless build.artifacts? + + build.keep_artifacts! + + status 200 + present build, with: ::API::Entities::Ci::Job + end + + desc 'Delete the artifacts files from a job' do + detail 'This feature was introduced in GitLab 11.9' + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + delete ':id/jobs/:job_id/artifacts' do + authorize_destroy_artifacts! + build = find_build!(params[:job_id]) + authorize!(:destroy_artifacts, build) + + build.erase_erasable_artifacts! + + status :no_content + end + end + end + end +end diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb new file mode 100644 index 00000000000..eea1637c32a --- /dev/null +++ b/lib/api/ci/jobs.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true + +module API + module Ci + class Jobs < ::API::Base + include PaginationParams + before { authenticate! } + + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + params do + requires :id, type: String, desc: 'The ID of a project' + end + + helpers do + params :optional_scope do + optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', + values: ::CommitStatus::AVAILABLE_STATUSES, + coerce_with: ->(scope) { + case scope + when String + [scope] + when ::Hash + scope.values + when ::Array + scope + else + ['unknown'] + end + } + end + end + + desc 'Get a projects jobs' do + success Entities::Ci::Job + end + params do + use :optional_scope + use :pagination + end + # rubocop: disable CodeReuse/ActiveRecord + get ':id/jobs', feature_category: :continuous_integration do + authorize_read_builds! + + builds = user_project.builds.order('id DESC') + builds = filter_builds(builds, params[:scope]) + + builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project) + present paginate(builds), with: Entities::Ci::Job + end + # rubocop: enable CodeReuse/ActiveRecord + + desc 'Get a specific job of a project' do + success Entities::Ci::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + get ':id/jobs/:job_id', feature_category: :continuous_integration do + authorize_read_builds! + + build = find_build!(params[:job_id]) + + present build, with: Entities::Ci::Job + end + + # TODO: We should use `present_disk_file!` and leave this implementation for backward compatibility (when build trace + # is saved in the DB instead of file). But before that, we need to consider how to replace the value of + # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. + desc 'Get a trace of a specific job of a project' + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + get ':id/jobs/:job_id/trace', feature_category: :continuous_integration do + authorize_read_builds! + + build = find_build!(params[:job_id]) + + authorize_read_build_trace!(build) if build + + header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" + content_type 'text/plain' + env['api.format'] = :binary + + # The trace can be nil bu body method expects a string as an argument. + trace = build.trace.raw || '' + body trace + end + + desc 'Cancel a specific job of a project' do + success Entities::Ci::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + post ':id/jobs/:job_id/cancel', feature_category: :continuous_integration do + authorize_update_builds! + + build = find_build!(params[:job_id]) + authorize!(:update_build, build) + + build.cancel + + present build, with: Entities::Ci::Job + end + + desc 'Retry a specific build of a project' do + success Entities::Ci::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a build' + end + post ':id/jobs/:job_id/retry', feature_category: :continuous_integration do + authorize_update_builds! + + build = find_build!(params[:job_id]) + authorize!(:update_build, build) + break forbidden!('Job is not retryable') unless build.retryable? + + build = ::Ci::Build.retry(build, current_user) + + present build, with: Entities::Ci::Job + end + + desc 'Erase job (remove artifacts and the trace)' do + success Entities::Ci::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a build' + end + post ':id/jobs/:job_id/erase', feature_category: :continuous_integration do + authorize_update_builds! + + build = find_build!(params[:job_id]) + authorize!(:erase_build, build) + break forbidden!('Job is not erasable!') unless build.erasable? + + build.erase(erased_by: current_user) + present build, with: Entities::Ci::Job + end + + desc 'Trigger an actionable job (manual, delayed, etc)' do + success Entities::Ci::JobBasic + detail 'This feature was added in GitLab 8.11' + end + params do + requires :job_id, type: Integer, desc: 'The ID of a Job' + end + + post ":id/jobs/:job_id/play", feature_category: :continuous_integration do + authorize_read_builds! + + job = find_job!(params[:job_id]) + + authorize!(:play_job, job) + + bad_request!("Unplayable Job") unless job.playable? + + job.play(current_user) + + status 200 + + if job.is_a?(::Ci::Build) + present job, with: Entities::Ci::Job + else + present job, with: Entities::Ci::Bridge + end + end + end + + resource :job do + desc 'Get current project using job token' do + success Entities::Ci::Job + end + route_setting :authentication, job_token_allowed: true + get '', feature_category: :continuous_integration do + validate_current_authenticated_job + + present current_authenticated_job, with: Entities::Ci::Job + end + end + + helpers do + # rubocop: disable CodeReuse/ActiveRecord + def filter_builds(builds, scope) + return builds if scope.nil? || scope.empty? + + available_statuses = ::CommitStatus::AVAILABLE_STATUSES + + unknown = scope - available_statuses + render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? + + builds.where(status: available_statuses && scope) + end + # rubocop: enable CodeReuse/ActiveRecord + + def validate_current_authenticated_job + # current_authenticated_job will be nil if user is using + # a valid authentication (like PRIVATE-TOKEN) that is not CI_JOB_TOKEN + not_found!('Job') unless current_authenticated_job + end + end + end + end +end + +API::Ci::Jobs.prepend_mod_with('API::Ci::Jobs') diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index 0bac6fe2054..b931e16f811 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -3,7 +3,7 @@ module API module Ci class Runner < ::API::Base - helpers ::API::Helpers::Runner + helpers ::API::Ci::Helpers::Runner content_type :txt, 'text/plain' diff --git a/lib/api/ci/triggers.rb b/lib/api/ci/triggers.rb new file mode 100644 index 00000000000..6a2b16e1568 --- /dev/null +++ b/lib/api/ci/triggers.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +module API + module Ci + class Triggers < ::API::Base + include PaginationParams + + HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase + + feature_category :continuous_integration + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + desc 'Trigger a GitLab project pipeline' do + success Entities::Ci::Pipeline + end + params do + requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false + requires :token, type: String, desc: 'The unique token of trigger or job token' + optional :variables, type: Hash, desc: 'The list of variables to be injected into build' + end + post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do + Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20758') + + forbidden! if gitlab_pipeline_hook_request? + + # validate variables + params[:variables] = params[:variables].to_h + unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) } + render_api_error!('variables needs to be a map of key-valued strings', 400) + end + + project = find_project(params[:id]) + not_found! unless project + + result = ::Ci::PipelineTriggerService.new(project, nil, params).execute + not_found! unless result + + if result.error? + render_api_error!(result[:message], result[:http_status]) + else + present result[:pipeline], with: Entities::Ci::Pipeline + end + end + + desc 'Get triggers list' do + success Entities::Trigger + end + params do + use :pagination + end + # rubocop: disable CodeReuse/ActiveRecord + get ':id/triggers' do + authenticate! + authorize! :admin_build, user_project + + triggers = user_project.triggers.includes(:trigger_requests) + + present paginate(triggers), with: Entities::Trigger, current_user: current_user + end + # rubocop: enable CodeReuse/ActiveRecord + + desc 'Get specific trigger of a project' do + success Entities::Trigger + end + params do + requires :trigger_id, type: Integer, desc: 'The trigger ID' + end + get ':id/triggers/:trigger_id' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.find(params.delete(:trigger_id)) + break not_found!('Trigger') unless trigger + + present trigger, with: Entities::Trigger, current_user: current_user + end + + desc 'Create a trigger' do + success Entities::Trigger + end + params do + requires :description, type: String, desc: 'The trigger description' + end + post ':id/triggers' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.create( + declared_params(include_missing: false).merge(owner: current_user)) + + if trigger.valid? + present trigger, with: Entities::Trigger, current_user: current_user + else + render_validation_error!(trigger) + end + end + + desc 'Update a trigger' do + success Entities::Trigger + end + params do + requires :trigger_id, type: Integer, desc: 'The trigger ID' + optional :description, type: String, desc: 'The trigger description' + end + put ':id/triggers/:trigger_id' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.find(params.delete(:trigger_id)) + break not_found!('Trigger') unless trigger + + authorize! :admin_trigger, trigger + + if trigger.update(declared_params(include_missing: false)) + present trigger, with: Entities::Trigger, current_user: current_user + else + render_validation_error!(trigger) + end + end + + desc 'Delete a trigger' do + success Entities::Trigger + end + params do + requires :trigger_id, type: Integer, desc: 'The trigger ID' + end + delete ':id/triggers/:trigger_id' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.find(params.delete(:trigger_id)) + break not_found!('Trigger') unless trigger + + destroy_conditionally!(trigger) + end + end + + helpers do + def gitlab_pipeline_hook_request? + request.get_header(HTTP_GITLAB_EVENT_HEADER) == WebHookService.hook_to_event(:pipeline_hooks) + end + end + end + end +end diff --git a/lib/api/ci/variables.rb b/lib/api/ci/variables.rb new file mode 100644 index 00000000000..9c04d5e9923 --- /dev/null +++ b/lib/api/ci/variables.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +module API + module Ci + class Variables < ::API::Base + include PaginationParams + + before { authenticate! } + before { authorize! :admin_build, user_project } + + feature_category :pipeline_authoring + + helpers ::API::Helpers::VariablesHelpers + + params do + requires :id, type: String, desc: 'The ID of a project' + end + + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + desc 'Get project variables' do + success Entities::Ci::Variable + end + params do + use :pagination + end + get ':id/variables' do + variables = user_project.variables + present paginate(variables), with: Entities::Ci::Variable + end + + desc 'Get a specific variable from a project' do + success Entities::Ci::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end + # rubocop: disable CodeReuse/ActiveRecord + get ':id/variables/:key' do + variable = find_variable(user_project, params) + not_found!('Variable') unless variable + + present variable, with: Entities::Ci::Variable + end + # rubocop: enable CodeReuse/ActiveRecord + + desc 'Create a new variable in a project' do + success Entities::Ci::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + 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 :environment_scope, type: String, desc: 'The environment_scope of the variable' + end + post ':id/variables' do + variable = ::Ci::ChangeVariableService.new( + container: user_project, + current_user: current_user, + params: { action: :create, variable_params: declared_params(include_missing: false) } + ).execute + + if variable.valid? + present variable, with: Entities::Ci::Variable + else + render_validation_error!(variable) + end + end + + desc 'Update an existing variable from a project' do + success Entities::Ci::Variable + end + params do + optional :key, type: String, desc: 'The key of the variable' + 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 :environment_scope, type: String, desc: 'The environment_scope of the variable' + optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' + end + # rubocop: disable CodeReuse/ActiveRecord + put ':id/variables/:key' do + variable = find_variable(user_project, params) + not_found!('Variable') unless variable + + variable = ::Ci::ChangeVariableService.new( + container: user_project, + current_user: current_user, + params: { action: :update, variable: variable, variable_params: declared_params(include_missing: false).except(:key, :filter) } + ).execute + + if variable.valid? + present variable, with: Entities::Ci::Variable + else + render_validation_error!(variable) + end + end + # rubocop: enable CodeReuse/ActiveRecord + + desc 'Delete an existing variable from a project' do + success Entities::Ci::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' + end + # rubocop: disable CodeReuse/ActiveRecord + delete ':id/variables/:key' do + variable = find_variable(user_project, params) + not_found!('Variable') unless variable + + ::Ci::ChangeVariableService.new( + container: user_project, + current_user: current_user, + params: { action: :destroy, variable: variable } + ).execute + + no_content! + end + # rubocop: enable CodeReuse/ActiveRecord + end + end + end +end diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb index 8d52a0a5b4e..13daf05fc78 100644 --- a/lib/api/group_variables.rb +++ b/lib/api/group_variables.rb @@ -8,7 +8,7 @@ module API before { authorize! :admin_group, user_group } feature_category :continuous_integration - helpers Helpers::VariablesHelpers + helpers ::API::Helpers::VariablesHelpers params do requires :id, type: String, desc: 'The ID of a group' diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 272452bd8db..0fdd6c141a9 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -132,7 +132,10 @@ module API :forking_access_level, :issues_access_level, :lfs_enabled, + :merge_pipelines_enabled, :merge_requests_access_level, + :merge_requests_template, + :merge_trains_enabled, :merge_method, :name, :only_allow_merge_if_all_discussions_are_resolved, diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb deleted file mode 100644 index a022d1a56ac..00000000000 --- a/lib/api/helpers/runner.rb +++ /dev/null @@ -1,121 +0,0 @@ -# frozen_string_literal: true - -module API - module Helpers - module Runner - include Gitlab::Utils::StrongMemoize - - prepend_mod_with('API::Helpers::Runner') # rubocop: disable Cop/InjectEnterpriseEditionModule - - JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN' - JOB_TOKEN_PARAM = :token - - def runner_registration_token_valid? - ActiveSupport::SecurityUtils.secure_compare(params[:token], Gitlab::CurrentSettings.runners_registration_token) - end - - def runner_registrar_valid?(type) - Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?(type) - end - - def authenticate_runner! - forbidden! unless current_runner - - current_runner - .heartbeat(get_runner_details_from_request) - end - - def get_runner_details_from_request - return get_runner_ip unless params['info'].present? - - attributes_for_keys(%w(name version revision platform architecture), params['info']) - .merge(get_runner_config_from_request) - .merge(get_runner_ip) - end - - def get_runner_ip - { ip_address: ip_address } - end - - def current_runner - token = params[:token] - - if token - ::Gitlab::Database::LoadBalancing::RackMiddleware - .stick_or_unstick(env, :runner, token) - end - - strong_memoize(:current_runner) do - ::Ci::Runner.find_by_token(token.to_s) - end - end - - # HTTP status codes to terminate the job on GitLab Runner: - # - 403 - def authenticate_job!(require_running: true) - job = current_job - - # 404 is not returned here because we want to terminate the job if it's - # running. A 404 can be returned from anywhere in the networking stack which is why - # we are explicit about a 403, we should improve this in - # https://gitlab.com/gitlab-org/gitlab/-/issues/327703 - forbidden! unless job - - forbidden! unless job_token_valid?(job) - - forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete? - forbidden!('Job has been erased!') if job.erased? - - if require_running - job_forbidden!(job, 'Job is not running') unless job.running? - end - - job.runner&.heartbeat(get_runner_ip) - - job - end - - def current_job - id = params[:id] - - if id - ::Gitlab::Database::LoadBalancing::RackMiddleware - .stick_or_unstick(env, :build, id) - end - - strong_memoize(:current_job) do - ::Ci::Build.find_by_id(id) - end - end - - def job_token_valid?(job) - token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s - token && job.valid_token?(token) - end - - def job_forbidden!(job, reason) - header 'Job-Status', job.status - forbidden!(reason) - end - - def set_application_context - return unless current_job - - Gitlab::ApplicationContext.push( - user: -> { current_job.user }, - project: -> { current_job.project } - ) - end - - def track_ci_minutes_usage!(_build, _runner) - # noop: overridden in EE - end - - private - - def get_runner_config_from_request - { config: attributes_for_keys(%w(gpus), params.dig('info', 'config')) } - end - end - end -end diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb deleted file mode 100644 index beda4433e4f..00000000000 --- a/lib/api/job_artifacts.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true - -module API - class JobArtifacts < ::API::Base - before { authenticate_non_get! } - - feature_category :build_artifacts - - # EE::API::JobArtifacts would override the following helpers - helpers do - def authorize_download_artifacts! - authorize_read_builds! - end - end - - prepend_mod_with('API::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule - - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - desc 'Download the artifacts archive from a job' do - detail 'This feature was introduced in GitLab 8.10' - end - params do - requires :ref_name, type: String, desc: 'The ref from repository' - requires :job, type: String, desc: 'The name for the job' - end - route_setting :authentication, job_token_allowed: true - get ':id/jobs/artifacts/:ref_name/download', - requirements: { ref_name: /.+/ } do - authorize_download_artifacts! - - latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) - authorize_read_job_artifacts!(latest_build) - - present_carrierwave_file!(latest_build.artifacts_file) - end - - desc 'Download a specific file from artifacts archive from a ref' do - detail 'This feature was introduced in GitLab 11.5' - end - params do - requires :ref_name, type: String, desc: 'The ref from repository' - requires :job, type: String, desc: 'The name for the job' - requires :artifact_path, type: String, desc: 'Artifact path' - end - route_setting :authentication, job_token_allowed: true - get ':id/jobs/artifacts/:ref_name/raw/*artifact_path', - format: false, - requirements: { ref_name: /.+/ } do - authorize_download_artifacts! - - build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) - authorize_read_job_artifacts!(build) - - path = Gitlab::Ci::Build::Artifacts::Path - .new(params[:artifact_path]) - - bad_request! unless path.valid? - - send_artifacts_entry(build.artifacts_file, path) - end - - desc 'Download the artifacts archive from a job' do - detail 'This feature was introduced in GitLab 8.5' - end - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - end - route_setting :authentication, job_token_allowed: true - get ':id/jobs/:job_id/artifacts' do - authorize_download_artifacts! - - build = find_build!(params[:job_id]) - authorize_read_job_artifacts!(build) - - present_carrierwave_file!(build.artifacts_file) - end - - desc 'Download a specific file from artifacts archive' do - detail 'This feature was introduced in GitLab 10.0' - end - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - requires :artifact_path, type: String, desc: 'Artifact path' - end - route_setting :authentication, job_token_allowed: true - get ':id/jobs/:job_id/artifacts/*artifact_path', format: false do - authorize_download_artifacts! - - build = find_build!(params[:job_id]) - authorize_read_job_artifacts!(build) - - not_found! unless build.available_artifacts? - - path = Gitlab::Ci::Build::Artifacts::Path - .new(params[:artifact_path]) - - bad_request! unless path.valid? - - send_artifacts_entry(build.artifacts_file, path) - end - - desc 'Keep the artifacts to prevent them from being deleted' do - success ::API::Entities::Ci::Job - end - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - end - post ':id/jobs/:job_id/artifacts/keep' do - authorize_update_builds! - - build = find_build!(params[:job_id]) - authorize!(:update_build, build) - break not_found!(build) unless build.artifacts? - - build.keep_artifacts! - - status 200 - present build, with: ::API::Entities::Ci::Job - end - - desc 'Delete the artifacts files from a job' do - detail 'This feature was introduced in GitLab 11.9' - end - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - end - delete ':id/jobs/:job_id/artifacts' do - authorize_destroy_artifacts! - build = find_build!(params[:job_id]) - authorize!(:destroy_artifacts, build) - - build.erase_erasable_artifacts! - - status :no_content - end - end - end -end diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb deleted file mode 100644 index 723a5b0fa3a..00000000000 --- a/lib/api/jobs.rb +++ /dev/null @@ -1,204 +0,0 @@ -# frozen_string_literal: true - -module API - class Jobs < ::API::Base - include PaginationParams - before { authenticate! } - - resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - params do - requires :id, type: String, desc: 'The ID of a project' - end - - helpers do - params :optional_scope do - optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', - values: ::CommitStatus::AVAILABLE_STATUSES, - coerce_with: ->(scope) { - case scope - when String - [scope] - when ::Hash - scope.values - when ::Array - scope - else - ['unknown'] - end - } - end - end - - desc 'Get a projects jobs' do - success Entities::Ci::Job - end - params do - use :optional_scope - use :pagination - end - # rubocop: disable CodeReuse/ActiveRecord - get ':id/jobs', feature_category: :continuous_integration do - authorize_read_builds! - - builds = user_project.builds.order('id DESC') - builds = filter_builds(builds, params[:scope]) - - builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project) - present paginate(builds), with: Entities::Ci::Job - end - # rubocop: enable CodeReuse/ActiveRecord - - desc 'Get a specific job of a project' do - success Entities::Ci::Job - end - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - end - get ':id/jobs/:job_id', feature_category: :continuous_integration do - authorize_read_builds! - - build = find_build!(params[:job_id]) - - present build, with: Entities::Ci::Job - end - - # TODO: We should use `present_disk_file!` and leave this implementation for backward compatibility (when build trace - # is saved in the DB instead of file). But before that, we need to consider how to replace the value of - # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. - desc 'Get a trace of a specific job of a project' - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - end - get ':id/jobs/:job_id/trace', feature_category: :continuous_integration do - authorize_read_builds! - - build = find_build!(params[:job_id]) - - authorize_read_build_trace!(build) if build - - header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" - content_type 'text/plain' - env['api.format'] = :binary - - # The trace can be nil bu body method expects a string as an argument. - trace = build.trace.raw || '' - body trace - end - - desc 'Cancel a specific job of a project' do - success Entities::Ci::Job - end - params do - requires :job_id, type: Integer, desc: 'The ID of a job' - end - post ':id/jobs/:job_id/cancel', feature_category: :continuous_integration do - authorize_update_builds! - - build = find_build!(params[:job_id]) - authorize!(:update_build, build) - - build.cancel - - present build, with: Entities::Ci::Job - end - - desc 'Retry a specific build of a project' do - success Entities::Ci::Job - end - params do - requires :job_id, type: Integer, desc: 'The ID of a build' - end - post ':id/jobs/:job_id/retry', feature_category: :continuous_integration do - authorize_update_builds! - - build = find_build!(params[:job_id]) - authorize!(:update_build, build) - break forbidden!('Job is not retryable') unless build.retryable? - - build = ::Ci::Build.retry(build, current_user) - - present build, with: Entities::Ci::Job - end - - desc 'Erase job (remove artifacts and the trace)' do - success Entities::Ci::Job - end - params do - requires :job_id, type: Integer, desc: 'The ID of a build' - end - post ':id/jobs/:job_id/erase', feature_category: :continuous_integration do - authorize_update_builds! - - build = find_build!(params[:job_id]) - authorize!(:erase_build, build) - break forbidden!('Job is not erasable!') unless build.erasable? - - build.erase(erased_by: current_user) - present build, with: Entities::Ci::Job - end - - desc 'Trigger an actionable job (manual, delayed, etc)' do - success Entities::Ci::JobBasic - detail 'This feature was added in GitLab 8.11' - end - params do - requires :job_id, type: Integer, desc: 'The ID of a Job' - end - - post ":id/jobs/:job_id/play", feature_category: :continuous_integration do - authorize_read_builds! - - job = find_job!(params[:job_id]) - - authorize!(:play_job, job) - - bad_request!("Unplayable Job") unless job.playable? - - job.play(current_user) - - status 200 - - if job.is_a?(::Ci::Build) - present job, with: Entities::Ci::Job - else - present job, with: Entities::Ci::Bridge - end - end - end - - resource :job do - desc 'Get current project using job token' do - success Entities::Ci::Job - end - route_setting :authentication, job_token_allowed: true - get '', feature_category: :continuous_integration do - validate_current_authenticated_job - - present current_authenticated_job, with: Entities::Ci::Job - end - end - - helpers do - # rubocop: disable CodeReuse/ActiveRecord - def filter_builds(builds, scope) - return builds if scope.nil? || scope.empty? - - available_statuses = ::CommitStatus::AVAILABLE_STATUSES - - unknown = scope - available_statuses - render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? - - builds.where(status: available_statuses && scope) - end - # rubocop: enable CodeReuse/ActiveRecord - - def validate_current_authenticated_job - # current_authenticated_job will be nil if user is using - # a valid authentication (like PRIVATE-TOKEN) that is not CI_JOB_TOKEN - not_found!('Job') unless current_authenticated_job - end - end - end -end - -API::Jobs.prepend_mod_with('API::Jobs') diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb deleted file mode 100644 index a359083a9d2..00000000000 --- a/lib/api/triggers.rb +++ /dev/null @@ -1,146 +0,0 @@ -# frozen_string_literal: true - -module API - class Triggers < ::API::Base - include PaginationParams - - HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase - - feature_category :continuous_integration - - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - desc 'Trigger a GitLab project pipeline' do - success Entities::Ci::Pipeline - end - params do - requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false - requires :token, type: String, desc: 'The unique token of trigger or job token' - optional :variables, type: Hash, desc: 'The list of variables to be injected into build' - end - post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do - Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20758') - - forbidden! if gitlab_pipeline_hook_request? - - # validate variables - params[:variables] = params[:variables].to_h - unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) } - render_api_error!('variables needs to be a map of key-valued strings', 400) - end - - project = find_project(params[:id]) - not_found! unless project - - result = ::Ci::PipelineTriggerService.new(project, nil, params).execute - not_found! unless result - - if result.error? - render_api_error!(result[:message], result[:http_status]) - else - present result[:pipeline], with: Entities::Ci::Pipeline - end - end - - desc 'Get triggers list' do - success Entities::Trigger - end - params do - use :pagination - end - # rubocop: disable CodeReuse/ActiveRecord - get ':id/triggers' do - authenticate! - authorize! :admin_build, user_project - - triggers = user_project.triggers.includes(:trigger_requests) - - present paginate(triggers), with: Entities::Trigger, current_user: current_user - end - # rubocop: enable CodeReuse/ActiveRecord - - desc 'Get specific trigger of a project' do - success Entities::Trigger - end - params do - requires :trigger_id, type: Integer, desc: 'The trigger ID' - end - get ':id/triggers/:trigger_id' do - authenticate! - authorize! :admin_build, user_project - - trigger = user_project.triggers.find(params.delete(:trigger_id)) - break not_found!('Trigger') unless trigger - - present trigger, with: Entities::Trigger, current_user: current_user - end - - desc 'Create a trigger' do - success Entities::Trigger - end - params do - requires :description, type: String, desc: 'The trigger description' - end - post ':id/triggers' do - authenticate! - authorize! :admin_build, user_project - - trigger = user_project.triggers.create( - declared_params(include_missing: false).merge(owner: current_user)) - - if trigger.valid? - present trigger, with: Entities::Trigger, current_user: current_user - else - render_validation_error!(trigger) - end - end - - desc 'Update a trigger' do - success Entities::Trigger - end - params do - requires :trigger_id, type: Integer, desc: 'The trigger ID' - optional :description, type: String, desc: 'The trigger description' - end - put ':id/triggers/:trigger_id' do - authenticate! - authorize! :admin_build, user_project - - trigger = user_project.triggers.find(params.delete(:trigger_id)) - break not_found!('Trigger') unless trigger - - authorize! :admin_trigger, trigger - - if trigger.update(declared_params(include_missing: false)) - present trigger, with: Entities::Trigger, current_user: current_user - else - render_validation_error!(trigger) - end - end - - desc 'Delete a trigger' do - success Entities::Trigger - end - params do - requires :trigger_id, type: Integer, desc: 'The trigger ID' - end - delete ':id/triggers/:trigger_id' do - authenticate! - authorize! :admin_build, user_project - - trigger = user_project.triggers.find(params.delete(:trigger_id)) - break not_found!('Trigger') unless trigger - - destroy_conditionally!(trigger) - end - end - - helpers do - def gitlab_pipeline_hook_request? - request.get_header(HTTP_GITLAB_EVENT_HEADER) == WebHookService.hook_to_event(:pipeline_hooks) - end - end - end -end diff --git a/lib/api/variables.rb b/lib/api/variables.rb deleted file mode 100644 index 75df0e050a6..00000000000 --- a/lib/api/variables.rb +++ /dev/null @@ -1,124 +0,0 @@ -# frozen_string_literal: true - -module API - class Variables < ::API::Base - include PaginationParams - - before { authenticate! } - before { authorize! :admin_build, user_project } - - feature_category :pipeline_authoring - - helpers Helpers::VariablesHelpers - - params do - requires :id, type: String, desc: 'The ID of a project' - end - - resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - desc 'Get project variables' do - success Entities::Ci::Variable - end - params do - use :pagination - end - get ':id/variables' do - variables = user_project.variables - present paginate(variables), with: Entities::Ci::Variable - end - - desc 'Get a specific variable from a project' do - success Entities::Ci::Variable - end - params do - requires :key, type: String, desc: 'The key of the variable' - end - # rubocop: disable CodeReuse/ActiveRecord - get ':id/variables/:key' do - variable = find_variable(user_project, params) - not_found!('Variable') unless variable - - present variable, with: Entities::Ci::Variable - end - # rubocop: enable CodeReuse/ActiveRecord - - desc 'Create a new variable in a project' do - success Entities::Ci::Variable - end - params do - requires :key, type: String, desc: 'The key of the variable' - 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 :environment_scope, type: String, desc: 'The environment_scope of the variable' - end - post ':id/variables' do - variable = ::Ci::ChangeVariableService.new( - container: user_project, - current_user: current_user, - params: { action: :create, variable_params: declared_params(include_missing: false) } - ).execute - - if variable.valid? - present variable, with: Entities::Ci::Variable - else - render_validation_error!(variable) - end - end - - desc 'Update an existing variable from a project' do - success Entities::Ci::Variable - end - params do - optional :key, type: String, desc: 'The key of the variable' - 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 :environment_scope, type: String, desc: 'The environment_scope of the variable' - optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' - end - # rubocop: disable CodeReuse/ActiveRecord - put ':id/variables/:key' do - variable = find_variable(user_project, params) - not_found!('Variable') unless variable - - variable = ::Ci::ChangeVariableService.new( - container: user_project, - current_user: current_user, - params: { action: :update, variable: variable, variable_params: declared_params(include_missing: false).except(:key, :filter) } - ).execute - - if variable.valid? - present variable, with: Entities::Ci::Variable - else - render_validation_error!(variable) - end - end - # rubocop: enable CodeReuse/ActiveRecord - - desc 'Delete an existing variable from a project' do - success Entities::Ci::Variable - end - params do - requires :key, type: String, desc: 'The key of the variable' - optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' - end - # rubocop: disable CodeReuse/ActiveRecord - delete ':id/variables/:key' do - variable = find_variable(user_project, params) - not_found!('Variable') unless variable - - ::Ci::ChangeVariableService.new( - container: user_project, - current_user: current_user, - params: { action: :destroy, variable: variable } - ).execute - - no_content! - end - # rubocop: enable CodeReuse/ActiveRecord - end - end -end diff --git a/spec/lib/api/helpers/runner_helpers_spec.rb b/spec/lib/api/ci/helpers/runner_helpers_spec.rb index 65b35845aab..c6638bea59e 100644 --- a/spec/lib/api/helpers/runner_helpers_spec.rb +++ b/spec/lib/api/ci/helpers/runner_helpers_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' -RSpec.describe API::Helpers::Runner do +RSpec.describe API::Ci::Helpers::Runner do let(:ip_address) { '1.2.3.4' } let(:runner_class) do Class.new do include API::Helpers - include API::Helpers::Runner + include API::Ci::Helpers::Runner attr_accessor :params diff --git a/spec/lib/api/helpers/runner_spec.rb b/spec/lib/api/ci/helpers/runner_spec.rb index e55c20b7ab6..99f2db544a5 100644 --- a/spec/lib/api/helpers/runner_spec.rb +++ b/spec/lib/api/ci/helpers/runner_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe API::Helpers::Runner do - let(:helper) { Class.new { include API::Helpers::Runner }.new } +RSpec.describe API::Ci::Helpers::Runner do + let(:helper) { Class.new { include API::Ci::Helpers::Runner }.new } before do allow(helper).to receive(:env).and_return({}) diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb index cff006bed94..b6ab9310471 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/ci/jobs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe API::Jobs do +RSpec.describe API::Ci::Jobs do include HttpBasicAuthHelpers include DependencyProxyHelpers @@ -114,7 +114,7 @@ RSpec.describe API::Jobs do context 'with job token authentication header' do include_context 'with auth headers' do - let(:header) { { API::Helpers::Runner::JOB_TOKEN_HEADER => running_job.token } } + let(:header) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => running_job.token } } end it_behaves_like 'returns common job data' do @@ -150,7 +150,7 @@ RSpec.describe API::Jobs do context 'with non running job' do include_context 'with auth headers' do - let(:header) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token } } + let(:header) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token } } end it_behaves_like 'returns unauthorized' @@ -523,15 +523,13 @@ RSpec.describe API::Jobs do context 'when artifacts are stored remotely' do let(:proxy_download) { false } + let(:job) { create(:ci_build, pipeline: pipeline) } + let(:artifact) { create(:ci_job_artifact, :archive, :remote_store, job: job) } before do stub_artifacts_object_storage(proxy_download: proxy_download) - end - - let(:job) { create(:ci_build, pipeline: pipeline) } - let!(:artifact) { create(:ci_job_artifact, :archive, :remote_store, job: job) } - before do + artifact job.reload get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user) @@ -708,11 +706,7 @@ RSpec.describe API::Jobs do context 'with branch name containing slash' do before do pipeline.reload - pipeline.update!(ref: 'improve/awesome', - sha: project.commit('improve/awesome').sha) - end - - before do + pipeline.update!(ref: 'improve/awesome', sha: project.commit('improve/awesome').sha) get_for_ref('improve/awesome') end diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb index 017a12a4a40..195aac2e5f0 100644 --- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb +++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb @@ -32,7 +32,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do let(:job) { create(:ci_build, :pending, user: user, project: project, pipeline: pipeline, runner_id: runner.id) } let(:jwt) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt } } - let(:headers_with_token) { headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.token) } + let(:headers_with_token) { headers.merge(API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token) } let(:file_upload) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } let(:file_upload2) { fixture_file_upload('spec/fixtures/dk.png', 'image/gif') } @@ -398,7 +398,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do context 'when using runners token' do it 'responds with forbidden' do - upload_artifacts(file_upload, headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.project.runners_token)) + upload_artifacts(file_upload, headers.merge(API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.project.runners_token)) expect(response).to have_gitlab_http_status(:forbidden) end diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb index e20c7e36096..2760e306693 100644 --- a/spec/requests/api/ci/runner/jobs_trace_spec.rb +++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb @@ -33,7 +33,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do project: project, user: user, runner_id: runner.id, pipeline: pipeline) end - let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } + let(:headers) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) } let(:update_interval) { 10.seconds.to_i } diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/ci/triggers_spec.rb index 4318f106996..410e2ae405e 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/ci/triggers_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe API::Triggers do +RSpec.describe API::Ci::Triggers do let_it_be(:user) { create(:user) } let_it_be(:user2) { create(:user) } diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/ci/variables_spec.rb index 1ae9b0d548d..dc524e121d5 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/ci/variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe API::Variables do +RSpec.describe API::Ci::Variables do let(:user) { create(:user) } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml index 8341fac3191..244c053df4a 100644 --- a/spec/requests/api/project_attributes.yml +++ b/spec/requests/api/project_attributes.yml @@ -89,6 +89,8 @@ ci_cd_settings: - group_runners_enabled - merge_pipelines_enabled - merge_trains_enabled + - merge_pipelines_enabled + - merge_trains_enabled - auto_rollback_enabled remapped_attributes: default_git_depth: ci_default_git_depth |