Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3e36f70be4
commit
411cc77938
|
@ -2,10 +2,6 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 12.7.2
|
||||
|
||||
- No changes.
|
||||
|
||||
## 12.7.1
|
||||
|
||||
### Fixed (6 changes)
|
||||
|
|
|
@ -20,6 +20,8 @@ import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
|||
import { __ } from '~/locale';
|
||||
import _ from 'underscore';
|
||||
|
||||
export const tableDataClass = 'table-col d-flex d-sm-table-cell';
|
||||
|
||||
export default {
|
||||
FIRST_PAGE: 1,
|
||||
PREV_PAGE: 1,
|
||||
|
@ -29,37 +31,37 @@ export default {
|
|||
key: 'error',
|
||||
label: __('Error'),
|
||||
thClass: 'w-60p',
|
||||
tdClass: 'table-col d-flex d-sm-table-cell px-3',
|
||||
tdClass: `${tableDataClass} px-3`,
|
||||
},
|
||||
{
|
||||
key: 'events',
|
||||
label: __('Events'),
|
||||
thClass: 'text-right',
|
||||
tdClass: 'table-col d-flex d-sm-table-cell',
|
||||
tdClass: `${tableDataClass}`,
|
||||
},
|
||||
{
|
||||
key: 'users',
|
||||
label: __('Users'),
|
||||
thClass: 'text-right',
|
||||
tdClass: 'table-col d-flex d-sm-table-cell',
|
||||
tdClass: `${tableDataClass}`,
|
||||
},
|
||||
{
|
||||
key: 'lastSeen',
|
||||
label: __('Last seen'),
|
||||
thClass: '',
|
||||
tdClass: 'table-col d-flex d-sm-table-cell',
|
||||
tdClass: `${tableDataClass}`,
|
||||
},
|
||||
{
|
||||
key: 'ignore',
|
||||
label: '',
|
||||
thClass: 'w-3rem',
|
||||
tdClass: 'table-col d-flex pl-0 d-sm-table-cell',
|
||||
tdClass: `${tableDataClass} pl-0`,
|
||||
},
|
||||
{
|
||||
key: 'resolved',
|
||||
label: '',
|
||||
thClass: 'w-3rem',
|
||||
tdClass: 'table-col d-flex pl-0 d-sm-table-cell',
|
||||
tdClass: `${tableDataClass} pl-0`,
|
||||
},
|
||||
{
|
||||
key: 'details',
|
||||
|
|
|
@ -119,6 +119,7 @@ export default {
|
|||
:class="retryButtonClass"
|
||||
:href="job.retry_path"
|
||||
data-method="post"
|
||||
data-qa-selector="retry_button"
|
||||
rel="nofollow"
|
||||
>{{ __('Retry') }}</gl-link
|
||||
>
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::GitHttpClientController < Projects::ApplicationController
|
||||
include ActionController::HttpAuthentication::Basic
|
||||
include KerberosSpnegoHelper
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :authentication_result, :redirected_path
|
||||
|
||||
delegate :actor, :authentication_abilities, to: :authentication_result, allow_nil: true
|
||||
delegate :type, to: :authentication_result, allow_nil: true, prefix: :auth_result
|
||||
|
||||
alias_method :user, :actor
|
||||
alias_method :authenticated_user, :actor
|
||||
|
||||
# Git clients will not know what authenticity token to send along
|
||||
skip_around_action :set_session_storage
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_before_action :repository
|
||||
before_action :authenticate_user
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def authenticate_user
|
||||
@authentication_result = Gitlab::Auth::Result.new
|
||||
|
||||
if allow_basic_auth? && basic_auth_provided?
|
||||
login, password = user_name_and_password(request)
|
||||
|
||||
if handle_basic_authentication(login, password)
|
||||
return # Allow access
|
||||
end
|
||||
elsif allow_kerberos_spnego_auth? && spnego_provided?
|
||||
kerberos_user = find_kerberos_user
|
||||
|
||||
if kerberos_user
|
||||
@authentication_result = Gitlab::Auth::Result.new(
|
||||
kerberos_user, nil, :kerberos, Gitlab::Auth.full_authentication_abilities)
|
||||
|
||||
send_final_spnego_response
|
||||
return # Allow access
|
||||
end
|
||||
elsif http_download_allowed?
|
||||
|
||||
@authentication_result = Gitlab::Auth::Result.new(nil, project, :none, [:download_code])
|
||||
|
||||
return # Allow access
|
||||
end
|
||||
|
||||
send_challenges
|
||||
render plain: "HTTP Basic: Access denied\n", status: :unauthorized
|
||||
rescue Gitlab::Auth::MissingPersonalAccessTokenError
|
||||
render_missing_personal_access_token
|
||||
end
|
||||
|
||||
def basic_auth_provided?
|
||||
has_basic_credentials?(request)
|
||||
end
|
||||
|
||||
def send_challenges
|
||||
challenges = []
|
||||
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
|
||||
challenges << spnego_challenge if allow_kerberos_spnego_auth?
|
||||
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
|
||||
end
|
||||
|
||||
def project
|
||||
parse_repo_path unless defined?(@project)
|
||||
|
||||
@project
|
||||
end
|
||||
|
||||
def parse_repo_path
|
||||
@project, @repo_type, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
|
||||
end
|
||||
|
||||
def render_missing_personal_access_token
|
||||
render plain: "HTTP Basic: Access denied\n" \
|
||||
"You must use a personal access token with 'read_repository' or 'write_repository' scope for Git over HTTP.\n" \
|
||||
"You can generate one at #{profile_personal_access_tokens_url}",
|
||||
status: :unauthorized
|
||||
end
|
||||
|
||||
def repository
|
||||
strong_memoize(:repository) do
|
||||
repo_type.repository_for(project)
|
||||
end
|
||||
end
|
||||
|
||||
def repo_type
|
||||
parse_repo_path unless defined?(@repo_type)
|
||||
|
||||
@repo_type
|
||||
end
|
||||
|
||||
def handle_basic_authentication(login, password)
|
||||
@authentication_result = Gitlab::Auth.find_for_git_client(
|
||||
login, password, project: project, ip: request.ip)
|
||||
|
||||
@authentication_result.success?
|
||||
end
|
||||
|
||||
def ci?
|
||||
authentication_result.ci?(project)
|
||||
end
|
||||
|
||||
def http_download_allowed?
|
||||
Gitlab::ProtocolAccess.allowed?('http') &&
|
||||
download_request? &&
|
||||
project && Guest.can?(:download_code, project)
|
||||
end
|
||||
end
|
||||
|
||||
Projects::GitHttpClientController.prepend_if_ee('EE::Projects::GitHttpClientController')
|
|
@ -1,117 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::GitHttpController < Projects::GitHttpClientController
|
||||
include WorkhorseRequest
|
||||
|
||||
before_action :access_check
|
||||
prepend_before_action :deny_head_requests, only: [:info_refs]
|
||||
|
||||
rescue_from Gitlab::GitAccess::UnauthorizedError, with: :render_403_with_exception
|
||||
rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception
|
||||
rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422_with_exception
|
||||
rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception
|
||||
|
||||
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
|
||||
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
|
||||
def info_refs
|
||||
log_user_activity if upload_pack?
|
||||
|
||||
render_ok
|
||||
end
|
||||
|
||||
# POST /foo/bar.git/git-upload-pack (git pull)
|
||||
def git_upload_pack
|
||||
enqueue_fetch_statistics_update
|
||||
|
||||
render_ok
|
||||
end
|
||||
|
||||
# POST /foo/bar.git/git-receive-pack" (git push)
|
||||
def git_receive_pack
|
||||
render_ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def deny_head_requests
|
||||
head :forbidden if request.head?
|
||||
end
|
||||
|
||||
def download_request?
|
||||
upload_pack?
|
||||
end
|
||||
|
||||
def upload_pack?
|
||||
git_command == 'git-upload-pack'
|
||||
end
|
||||
|
||||
def git_command
|
||||
if action_name == 'info_refs'
|
||||
params[:service]
|
||||
else
|
||||
action_name.dasherize
|
||||
end
|
||||
end
|
||||
|
||||
def render_ok
|
||||
set_workhorse_internal_api_content_type
|
||||
render json: Gitlab::Workhorse.git_http_ok(repository, repo_type, user, action_name)
|
||||
end
|
||||
|
||||
def render_403_with_exception(exception)
|
||||
render plain: exception.message, status: :forbidden
|
||||
end
|
||||
|
||||
def render_404_with_exception(exception)
|
||||
render plain: exception.message, status: :not_found
|
||||
end
|
||||
|
||||
def render_422_with_exception(exception)
|
||||
render plain: exception.message, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def render_503_with_exception(exception)
|
||||
render plain: exception.message, status: :service_unavailable
|
||||
end
|
||||
|
||||
def enqueue_fetch_statistics_update
|
||||
return if Gitlab::Database.read_only?
|
||||
return if repo_type.wiki?
|
||||
return unless project&.daily_statistics_enabled?
|
||||
|
||||
ProjectDailyStatisticsWorker.perform_async(project.id)
|
||||
end
|
||||
|
||||
def access
|
||||
@access ||= access_klass.new(access_actor, project, 'http',
|
||||
authentication_abilities: authentication_abilities,
|
||||
namespace_path: params[:namespace_id],
|
||||
project_path: project_path,
|
||||
redirected_path: redirected_path,
|
||||
auth_result_type: auth_result_type)
|
||||
end
|
||||
|
||||
def access_actor
|
||||
return user if user
|
||||
return :ci if ci?
|
||||
end
|
||||
|
||||
def access_check
|
||||
access.check(git_command, Gitlab::GitAccess::ANY)
|
||||
@project ||= access.project
|
||||
end
|
||||
|
||||
def access_klass
|
||||
@access_klass ||= repo_type.access_checker_class
|
||||
end
|
||||
|
||||
def project_path
|
||||
@project_path ||= params[:project_id].sub(/\.git$/, '')
|
||||
end
|
||||
|
||||
def log_user_activity
|
||||
Users::ActivityService.new(user).execute
|
||||
end
|
||||
end
|
||||
|
||||
Projects::GitHttpController.prepend_if_ee('EE::Projects::GitHttpController')
|
|
@ -1,140 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::LfsApiController < Projects::GitHttpClientController
|
||||
include LfsRequest
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
LFS_TRANSFER_CONTENT_TYPE = 'application/octet-stream'
|
||||
|
||||
skip_before_action :lfs_check_access!, only: [:deprecated]
|
||||
before_action :lfs_check_batch_operation!, only: [:batch]
|
||||
|
||||
def batch
|
||||
unless objects.present?
|
||||
render_lfs_not_found
|
||||
return
|
||||
end
|
||||
|
||||
if download_request?
|
||||
render json: { objects: download_objects! }
|
||||
elsif upload_request?
|
||||
render json: { objects: upload_objects! }
|
||||
else
|
||||
raise "Never reached"
|
||||
end
|
||||
end
|
||||
|
||||
def deprecated
|
||||
render(
|
||||
json: {
|
||||
message: _('Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.'),
|
||||
documentation_url: "#{Gitlab.config.gitlab.url}/help"
|
||||
},
|
||||
status: :not_implemented
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
params[:operation] == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
params[:operation] == 'upload'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def existing_oids
|
||||
@existing_oids ||= begin
|
||||
project.all_lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def download_objects!
|
||||
objects.each do |object|
|
||||
if existing_oids.include?(object[:oid])
|
||||
object[:actions] = download_actions(object)
|
||||
|
||||
if Guest.can?(:download_code, project)
|
||||
object[:authenticated] = true
|
||||
end
|
||||
else
|
||||
object[:error] = {
|
||||
code: 404,
|
||||
message: _("Object does not exist on the server or you don't have permissions to access it")
|
||||
}
|
||||
end
|
||||
end
|
||||
objects
|
||||
end
|
||||
|
||||
def upload_objects!
|
||||
objects.each do |object|
|
||||
object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
|
||||
end
|
||||
objects
|
||||
end
|
||||
|
||||
def download_actions(object)
|
||||
{
|
||||
download: {
|
||||
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}",
|
||||
header: {
|
||||
Authorization: authorization_header
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def upload_actions(object)
|
||||
{
|
||||
upload: {
|
||||
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
|
||||
header: {
|
||||
Authorization: authorization_header,
|
||||
# git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
|
||||
# ensures that Workhorse can intercept the request.
|
||||
'Content-Type': LFS_TRANSFER_CONTENT_TYPE
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def lfs_check_batch_operation!
|
||||
if batch_operation_disallowed?
|
||||
render(
|
||||
json: {
|
||||
message: lfs_read_only_message
|
||||
},
|
||||
content_type: LfsRequest::CONTENT_TYPE,
|
||||
status: :forbidden
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def batch_operation_disallowed?
|
||||
upload_request? && Gitlab::Database.read_only?
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def lfs_read_only_message
|
||||
_('You cannot write to this read-only GitLab instance.')
|
||||
end
|
||||
|
||||
def authorization_header
|
||||
strong_memoize(:authorization_header) do
|
||||
lfs_auth_header || request.headers['Authorization']
|
||||
end
|
||||
end
|
||||
|
||||
def lfs_auth_header
|
||||
return unless user.is_a?(User)
|
||||
|
||||
Gitlab::LfsToken.new(user).basic_encoding
|
||||
end
|
||||
end
|
||||
|
||||
Projects::LfsApiController.prepend_if_ee('EE::Projects::LfsApiController')
|
|
@ -1,76 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::LfsLocksApiController < Projects::GitHttpClientController
|
||||
include LfsRequest
|
||||
|
||||
def create
|
||||
@result = Lfs::LockFileService.new(project, user, lfs_params).execute
|
||||
|
||||
render_json(@result[:lock])
|
||||
end
|
||||
|
||||
def unlock
|
||||
@result = Lfs::UnlockFileService.new(project, user, lfs_params).execute
|
||||
|
||||
render_json(@result[:lock])
|
||||
end
|
||||
|
||||
def index
|
||||
@result = Lfs::LocksFinderService.new(project, user, lfs_params).execute
|
||||
|
||||
render_json(@result[:locks])
|
||||
end
|
||||
|
||||
def verify
|
||||
@result = Lfs::LocksFinderService.new(project, user, {}).execute
|
||||
|
||||
ours, theirs = split_by_owner(@result[:locks])
|
||||
|
||||
render_json({ ours: ours, theirs: theirs }, false)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_json(data, process = true)
|
||||
render json: build_payload(data, process),
|
||||
content_type: LfsRequest::CONTENT_TYPE,
|
||||
status: @result[:http_status]
|
||||
end
|
||||
|
||||
def build_payload(data, process)
|
||||
data = LfsFileLockSerializer.new.represent(data) if process
|
||||
|
||||
return data if @result[:status] == :success
|
||||
|
||||
# When the locking failed due to an existent Lock, the existent record
|
||||
# is returned in `@result[:lock]`
|
||||
error_payload(@result[:message], @result[:lock] ? data : {})
|
||||
end
|
||||
|
||||
def error_payload(message, custom_attrs = {})
|
||||
custom_attrs.merge({
|
||||
message: message,
|
||||
documentation_url: help_url
|
||||
})
|
||||
end
|
||||
|
||||
def split_by_owner(locks)
|
||||
groups = locks.partition { |lock| lock.user_id == user.id }
|
||||
|
||||
groups.map! do |records|
|
||||
LfsFileLockSerializer.new.represent(records, root: false)
|
||||
end
|
||||
end
|
||||
|
||||
def download_request?
|
||||
params[:action] == 'index'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
%w(create unlock verify).include?(params[:action])
|
||||
end
|
||||
|
||||
def lfs_params
|
||||
params.permit(:id, :path, :force)
|
||||
end
|
||||
end
|
|
@ -1,89 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::LfsStorageController < Projects::GitHttpClientController
|
||||
include LfsRequest
|
||||
include WorkhorseRequest
|
||||
include SendFileUpload
|
||||
|
||||
skip_before_action :verify_workhorse_api!, only: :download
|
||||
|
||||
def download
|
||||
lfs_object = LfsObject.find_by_oid(oid)
|
||||
unless lfs_object && lfs_object.file.exists?
|
||||
render_lfs_not_found
|
||||
return
|
||||
end
|
||||
|
||||
send_upload(lfs_object.file, send_params: { content_type: "application/octet-stream" })
|
||||
end
|
||||
|
||||
def upload_authorize
|
||||
set_workhorse_internal_api_content_type
|
||||
|
||||
authorized = LfsObjectUploader.workhorse_authorize(has_length: true)
|
||||
authorized.merge!(LfsOid: oid, LfsSize: size)
|
||||
|
||||
render json: authorized
|
||||
end
|
||||
|
||||
def upload_finalize
|
||||
if store_file!(oid, size)
|
||||
head 200
|
||||
else
|
||||
render plain: 'Unprocessable entity', status: :unprocessable_entity
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render_lfs_forbidden
|
||||
rescue UploadedFile::InvalidPathError
|
||||
render_lfs_forbidden
|
||||
rescue ObjectStorage::RemoteStoreError
|
||||
render_lfs_forbidden
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
action_name == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
%w[upload_authorize upload_finalize].include? action_name
|
||||
end
|
||||
|
||||
def oid
|
||||
params[:oid].to_s
|
||||
end
|
||||
|
||||
def size
|
||||
params[:size].to_i
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def store_file!(oid, size)
|
||||
object = LfsObject.find_by(oid: oid, size: size)
|
||||
unless object&.file&.exists?
|
||||
object = create_file!(oid, size)
|
||||
end
|
||||
|
||||
return unless object
|
||||
|
||||
link_to_project!(object)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def create_file!(oid, size)
|
||||
uploaded_file = UploadedFile.from_params(
|
||||
params, :file, LfsObjectUploader.workhorse_local_upload_path)
|
||||
return unless uploaded_file
|
||||
|
||||
LfsObject.create!(oid: oid, size: size, file: uploaded_file)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def link_to_project!(object)
|
||||
if object && !object.projects.exists?(storage_project.id)
|
||||
object.lfs_objects_projects.create!(project: storage_project)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
|
@ -387,6 +387,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
:merge_method,
|
||||
:initialize_with_readme,
|
||||
:autoclose_referenced_issues,
|
||||
:suggestion_commit_message,
|
||||
|
||||
project_feature_attributes: %i[
|
||||
builds_access_level
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Repositories
|
||||
class ApplicationController < ::ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
end
|
||||
end
|
|
@ -0,0 +1,125 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Repositories
|
||||
class GitHttpClientController < Repositories::ApplicationController
|
||||
include ActionController::HttpAuthentication::Basic
|
||||
include KerberosSpnegoHelper
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :authentication_result, :redirected_path
|
||||
|
||||
delegate :actor, :authentication_abilities, to: :authentication_result, allow_nil: true
|
||||
delegate :type, to: :authentication_result, allow_nil: true, prefix: :auth_result
|
||||
|
||||
alias_method :user, :actor
|
||||
alias_method :authenticated_user, :actor
|
||||
|
||||
# Git clients will not know what authenticity token to send along
|
||||
skip_around_action :set_session_storage
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
||||
before_action :parse_repo_path
|
||||
before_action :authenticate_user
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def authenticate_user
|
||||
@authentication_result = Gitlab::Auth::Result.new
|
||||
|
||||
if allow_basic_auth? && basic_auth_provided?
|
||||
login, password = user_name_and_password(request)
|
||||
|
||||
if handle_basic_authentication(login, password)
|
||||
return # Allow access
|
||||
end
|
||||
elsif allow_kerberos_spnego_auth? && spnego_provided?
|
||||
kerberos_user = find_kerberos_user
|
||||
|
||||
if kerberos_user
|
||||
@authentication_result = Gitlab::Auth::Result.new(
|
||||
kerberos_user, nil, :kerberos, Gitlab::Auth.full_authentication_abilities)
|
||||
|
||||
send_final_spnego_response
|
||||
return # Allow access
|
||||
end
|
||||
elsif http_download_allowed?
|
||||
|
||||
@authentication_result = Gitlab::Auth::Result.new(nil, project, :none, [:download_code])
|
||||
|
||||
return # Allow access
|
||||
end
|
||||
|
||||
send_challenges
|
||||
render plain: "HTTP Basic: Access denied\n", status: :unauthorized
|
||||
rescue Gitlab::Auth::MissingPersonalAccessTokenError
|
||||
render_missing_personal_access_token
|
||||
end
|
||||
|
||||
def basic_auth_provided?
|
||||
has_basic_credentials?(request)
|
||||
end
|
||||
|
||||
def send_challenges
|
||||
challenges = []
|
||||
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
|
||||
challenges << spnego_challenge if allow_kerberos_spnego_auth?
|
||||
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
|
||||
end
|
||||
|
||||
def project
|
||||
parse_repo_path unless defined?(@project)
|
||||
|
||||
@project
|
||||
end
|
||||
|
||||
def parse_repo_path
|
||||
@project, @repo_type, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:repository_id]}")
|
||||
end
|
||||
|
||||
def render_missing_personal_access_token
|
||||
render plain: "HTTP Basic: Access denied\n" \
|
||||
"You must use a personal access token with 'read_repository' or 'write_repository' scope for Git over HTTP.\n" \
|
||||
"You can generate one at #{profile_personal_access_tokens_url}",
|
||||
status: :unauthorized
|
||||
end
|
||||
|
||||
def repository
|
||||
strong_memoize(:repository) do
|
||||
repo_type.repository_for(project)
|
||||
end
|
||||
end
|
||||
|
||||
def repo_type
|
||||
parse_repo_path unless defined?(@repo_type)
|
||||
|
||||
@repo_type
|
||||
end
|
||||
|
||||
def handle_basic_authentication(login, password)
|
||||
@authentication_result = Gitlab::Auth.find_for_git_client(
|
||||
login, password, project: project, ip: request.ip)
|
||||
|
||||
@authentication_result.success?
|
||||
end
|
||||
|
||||
def ci?
|
||||
authentication_result.ci?(project)
|
||||
end
|
||||
|
||||
def http_download_allowed?
|
||||
Gitlab::ProtocolAccess.allowed?('http') &&
|
||||
download_request? &&
|
||||
project && Guest.can?(:download_code, project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Repositories::GitHttpClientController.prepend_if_ee('EE::Repositories::GitHttpClientController')
|
|
@ -0,0 +1,119 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Repositories
|
||||
class GitHttpController < Repositories::GitHttpClientController
|
||||
include WorkhorseRequest
|
||||
|
||||
before_action :access_check
|
||||
prepend_before_action :deny_head_requests, only: [:info_refs]
|
||||
|
||||
rescue_from Gitlab::GitAccess::UnauthorizedError, with: :render_403_with_exception
|
||||
rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception
|
||||
rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422_with_exception
|
||||
rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception
|
||||
|
||||
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
|
||||
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
|
||||
def info_refs
|
||||
log_user_activity if upload_pack?
|
||||
|
||||
render_ok
|
||||
end
|
||||
|
||||
# POST /foo/bar.git/git-upload-pack (git pull)
|
||||
def git_upload_pack
|
||||
enqueue_fetch_statistics_update
|
||||
|
||||
render_ok
|
||||
end
|
||||
|
||||
# POST /foo/bar.git/git-receive-pack" (git push)
|
||||
def git_receive_pack
|
||||
render_ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def deny_head_requests
|
||||
head :forbidden if request.head?
|
||||
end
|
||||
|
||||
def download_request?
|
||||
upload_pack?
|
||||
end
|
||||
|
||||
def upload_pack?
|
||||
git_command == 'git-upload-pack'
|
||||
end
|
||||
|
||||
def git_command
|
||||
if action_name == 'info_refs'
|
||||
params[:service]
|
||||
else
|
||||
action_name.dasherize
|
||||
end
|
||||
end
|
||||
|
||||
def render_ok
|
||||
set_workhorse_internal_api_content_type
|
||||
render json: Gitlab::Workhorse.git_http_ok(repository, repo_type, user, action_name)
|
||||
end
|
||||
|
||||
def render_403_with_exception(exception)
|
||||
render plain: exception.message, status: :forbidden
|
||||
end
|
||||
|
||||
def render_404_with_exception(exception)
|
||||
render plain: exception.message, status: :not_found
|
||||
end
|
||||
|
||||
def render_422_with_exception(exception)
|
||||
render plain: exception.message, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def render_503_with_exception(exception)
|
||||
render plain: exception.message, status: :service_unavailable
|
||||
end
|
||||
|
||||
def enqueue_fetch_statistics_update
|
||||
return if Gitlab::Database.read_only?
|
||||
return unless repo_type.project?
|
||||
return unless project&.daily_statistics_enabled?
|
||||
|
||||
ProjectDailyStatisticsWorker.perform_async(project.id)
|
||||
end
|
||||
|
||||
def access
|
||||
@access ||= access_klass.new(access_actor, project, 'http',
|
||||
authentication_abilities: authentication_abilities,
|
||||
namespace_path: params[:namespace_id],
|
||||
project_path: project_path,
|
||||
redirected_path: redirected_path,
|
||||
auth_result_type: auth_result_type)
|
||||
end
|
||||
|
||||
def access_actor
|
||||
return user if user
|
||||
return :ci if ci?
|
||||
end
|
||||
|
||||
def access_check
|
||||
access.check(git_command, Gitlab::GitAccess::ANY)
|
||||
@project ||= access.project
|
||||
end
|
||||
|
||||
def access_klass
|
||||
@access_klass ||= repo_type.access_checker_class
|
||||
end
|
||||
|
||||
def project_path
|
||||
@project_path ||= params[:repository_id].sub(/\.git$/, '')
|
||||
end
|
||||
|
||||
def log_user_activity
|
||||
Users::ActivityService.new(user).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Repositories::GitHttpController.prepend_if_ee('EE::Repositories::GitHttpController')
|
|
@ -0,0 +1,142 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Repositories
|
||||
class LfsApiController < Repositories::GitHttpClientController
|
||||
include LfsRequest
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
LFS_TRANSFER_CONTENT_TYPE = 'application/octet-stream'
|
||||
|
||||
skip_before_action :lfs_check_access!, only: [:deprecated]
|
||||
before_action :lfs_check_batch_operation!, only: [:batch]
|
||||
|
||||
def batch
|
||||
unless objects.present?
|
||||
render_lfs_not_found
|
||||
return
|
||||
end
|
||||
|
||||
if download_request?
|
||||
render json: { objects: download_objects! }
|
||||
elsif upload_request?
|
||||
render json: { objects: upload_objects! }
|
||||
else
|
||||
raise "Never reached"
|
||||
end
|
||||
end
|
||||
|
||||
def deprecated
|
||||
render(
|
||||
json: {
|
||||
message: _('Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.'),
|
||||
documentation_url: "#{Gitlab.config.gitlab.url}/help"
|
||||
},
|
||||
status: :not_implemented
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
params[:operation] == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
params[:operation] == 'upload'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def existing_oids
|
||||
@existing_oids ||= begin
|
||||
project.all_lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def download_objects!
|
||||
objects.each do |object|
|
||||
if existing_oids.include?(object[:oid])
|
||||
object[:actions] = download_actions(object)
|
||||
|
||||
if Guest.can?(:download_code, project)
|
||||
object[:authenticated] = true
|
||||
end
|
||||
else
|
||||
object[:error] = {
|
||||
code: 404,
|
||||
message: _("Object does not exist on the server or you don't have permissions to access it")
|
||||
}
|
||||
end
|
||||
end
|
||||
objects
|
||||
end
|
||||
|
||||
def upload_objects!
|
||||
objects.each do |object|
|
||||
object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
|
||||
end
|
||||
objects
|
||||
end
|
||||
|
||||
def download_actions(object)
|
||||
{
|
||||
download: {
|
||||
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}",
|
||||
header: {
|
||||
Authorization: authorization_header
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def upload_actions(object)
|
||||
{
|
||||
upload: {
|
||||
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
|
||||
header: {
|
||||
Authorization: authorization_header,
|
||||
# git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
|
||||
# ensures that Workhorse can intercept the request.
|
||||
'Content-Type': LFS_TRANSFER_CONTENT_TYPE
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def lfs_check_batch_operation!
|
||||
if batch_operation_disallowed?
|
||||
render(
|
||||
json: {
|
||||
message: lfs_read_only_message
|
||||
},
|
||||
content_type: LfsRequest::CONTENT_TYPE,
|
||||
status: :forbidden
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def batch_operation_disallowed?
|
||||
upload_request? && Gitlab::Database.read_only?
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def lfs_read_only_message
|
||||
_('You cannot write to this read-only GitLab instance.')
|
||||
end
|
||||
|
||||
def authorization_header
|
||||
strong_memoize(:authorization_header) do
|
||||
lfs_auth_header || request.headers['Authorization']
|
||||
end
|
||||
end
|
||||
|
||||
def lfs_auth_header
|
||||
return unless user.is_a?(User)
|
||||
|
||||
Gitlab::LfsToken.new(user).basic_encoding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Repositories::LfsApiController.prepend_if_ee('EE::Repositories::LfsApiController')
|
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Repositories
|
||||
class LfsLocksApiController < Repositories::GitHttpClientController
|
||||
include LfsRequest
|
||||
|
||||
def create
|
||||
@result = Lfs::LockFileService.new(project, user, lfs_params).execute
|
||||
|
||||
render_json(@result[:lock])
|
||||
end
|
||||
|
||||
def unlock
|
||||
@result = Lfs::UnlockFileService.new(project, user, lfs_params).execute
|
||||
|
||||
render_json(@result[:lock])
|
||||
end
|
||||
|
||||
def index
|
||||
@result = Lfs::LocksFinderService.new(project, user, lfs_params).execute
|
||||
|
||||
render_json(@result[:locks])
|
||||
end
|
||||
|
||||
def verify
|
||||
@result = Lfs::LocksFinderService.new(project, user, {}).execute
|
||||
|
||||
ours, theirs = split_by_owner(@result[:locks])
|
||||
|
||||
render_json({ ours: ours, theirs: theirs }, false)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_json(data, process = true)
|
||||
render json: build_payload(data, process),
|
||||
content_type: LfsRequest::CONTENT_TYPE,
|
||||
status: @result[:http_status]
|
||||
end
|
||||
|
||||
def build_payload(data, process)
|
||||
data = LfsFileLockSerializer.new.represent(data) if process
|
||||
|
||||
return data if @result[:status] == :success
|
||||
|
||||
# When the locking failed due to an existent Lock, the existent record
|
||||
# is returned in `@result[:lock]`
|
||||
error_payload(@result[:message], @result[:lock] ? data : {})
|
||||
end
|
||||
|
||||
def error_payload(message, custom_attrs = {})
|
||||
custom_attrs.merge({
|
||||
message: message,
|
||||
documentation_url: help_url
|
||||
})
|
||||
end
|
||||
|
||||
def split_by_owner(locks)
|
||||
groups = locks.partition { |lock| lock.user_id == user.id }
|
||||
|
||||
groups.map! do |records|
|
||||
LfsFileLockSerializer.new.represent(records, root: false)
|
||||
end
|
||||
end
|
||||
|
||||
def download_request?
|
||||
params[:action] == 'index'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
%w(create unlock verify).include?(params[:action])
|
||||
end
|
||||
|
||||
def lfs_params
|
||||
params.permit(:id, :path, :force)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Repositories
|
||||
class LfsStorageController < Repositories::GitHttpClientController
|
||||
include LfsRequest
|
||||
include WorkhorseRequest
|
||||
include SendFileUpload
|
||||
|
||||
skip_before_action :verify_workhorse_api!, only: :download
|
||||
|
||||
def download
|
||||
lfs_object = LfsObject.find_by_oid(oid)
|
||||
unless lfs_object && lfs_object.file.exists?
|
||||
render_lfs_not_found
|
||||
return
|
||||
end
|
||||
|
||||
send_upload(lfs_object.file, send_params: { content_type: "application/octet-stream" })
|
||||
end
|
||||
|
||||
def upload_authorize
|
||||
set_workhorse_internal_api_content_type
|
||||
|
||||
authorized = LfsObjectUploader.workhorse_authorize(has_length: true)
|
||||
authorized.merge!(LfsOid: oid, LfsSize: size)
|
||||
|
||||
render json: authorized
|
||||
end
|
||||
|
||||
def upload_finalize
|
||||
if store_file!(oid, size)
|
||||
head 200
|
||||
else
|
||||
render plain: 'Unprocessable entity', status: :unprocessable_entity
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render_lfs_forbidden
|
||||
rescue UploadedFile::InvalidPathError
|
||||
render_lfs_forbidden
|
||||
rescue ObjectStorage::RemoteStoreError
|
||||
render_lfs_forbidden
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
action_name == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
%w[upload_authorize upload_finalize].include? action_name
|
||||
end
|
||||
|
||||
def oid
|
||||
params[:oid].to_s
|
||||
end
|
||||
|
||||
def size
|
||||
params[:size].to_i
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def store_file!(oid, size)
|
||||
object = LfsObject.find_by(oid: oid, size: size)
|
||||
unless object&.file&.exists?
|
||||
object = create_file!(oid, size)
|
||||
end
|
||||
|
||||
return unless object
|
||||
|
||||
link_to_project!(object)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def create_file!(oid, size)
|
||||
uploaded_file = UploadedFile.from_params(
|
||||
params, :file, LfsObjectUploader.workhorse_local_upload_path)
|
||||
return unless uploaded_file
|
||||
|
||||
LfsObject.create!(oid: oid, size: size, file: uploaded_file)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def link_to_project!(object)
|
||||
if object && !object.projects.exists?(storage_project.id)
|
||||
object.lfs_objects_projects.create!(project: storage_project)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
|
@ -11,77 +11,87 @@ module Types
|
|||
|
||||
field :id, GraphQL::ID_TYPE,
|
||||
null: false,
|
||||
description: "ID (global ID) of the error"
|
||||
description: 'ID (global ID) of the error'
|
||||
field :sentry_id, GraphQL::STRING_TYPE,
|
||||
method: :id,
|
||||
null: false,
|
||||
description: "ID (Sentry ID) of the error"
|
||||
description: 'ID (Sentry ID) of the error'
|
||||
field :title, GraphQL::STRING_TYPE,
|
||||
null: false,
|
||||
description: "Title of the error"
|
||||
description: 'Title of the error'
|
||||
field :type, GraphQL::STRING_TYPE,
|
||||
null: false,
|
||||
description: "Type of the error"
|
||||
description: 'Type of the error'
|
||||
field :user_count, GraphQL::INT_TYPE,
|
||||
null: false,
|
||||
description: "Count of users affected by the error"
|
||||
description: 'Count of users affected by the error'
|
||||
field :count, GraphQL::INT_TYPE,
|
||||
null: false,
|
||||
description: "Count of occurrences"
|
||||
description: 'Count of occurrences'
|
||||
field :first_seen, Types::TimeType,
|
||||
null: false,
|
||||
description: "Timestamp when the error was first seen"
|
||||
description: 'Timestamp when the error was first seen'
|
||||
field :last_seen, Types::TimeType,
|
||||
null: false,
|
||||
description: "Timestamp when the error was last seen"
|
||||
description: 'Timestamp when the error was last seen'
|
||||
field :message, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Sentry metadata message of the error"
|
||||
description: 'Sentry metadata message of the error'
|
||||
field :culprit, GraphQL::STRING_TYPE,
|
||||
null: false,
|
||||
description: "Culprit of the error"
|
||||
description: 'Culprit of the error'
|
||||
field :external_base_url, GraphQL::STRING_TYPE,
|
||||
null: false,
|
||||
description: 'External Base URL of the Sentry Instance'
|
||||
field :external_url, GraphQL::STRING_TYPE,
|
||||
null: false,
|
||||
description: "External URL of the error"
|
||||
description: 'External URL of the error'
|
||||
field :sentry_project_id, GraphQL::ID_TYPE,
|
||||
method: :project_id,
|
||||
null: false,
|
||||
description: "ID of the project (Sentry project)"
|
||||
description: 'ID of the project (Sentry project)'
|
||||
field :sentry_project_name, GraphQL::STRING_TYPE,
|
||||
method: :project_name,
|
||||
null: false,
|
||||
description: "Name of the project affected by the error"
|
||||
description: 'Name of the project affected by the error'
|
||||
field :sentry_project_slug, GraphQL::STRING_TYPE,
|
||||
method: :project_slug,
|
||||
null: false,
|
||||
description: "Slug of the project affected by the error"
|
||||
description: 'Slug of the project affected by the error'
|
||||
field :short_id, GraphQL::STRING_TYPE,
|
||||
null: false,
|
||||
description: "Short ID (Sentry ID) of the error"
|
||||
description: 'Short ID (Sentry ID) of the error'
|
||||
field :status, Types::ErrorTracking::SentryErrorStatusEnum,
|
||||
null: false,
|
||||
description: "Status of the error"
|
||||
description: 'Status of the error'
|
||||
field :frequency, [Types::ErrorTracking::SentryErrorFrequencyType],
|
||||
null: false,
|
||||
description: "Last 24hr stats of the error"
|
||||
description: 'Last 24hr stats of the error'
|
||||
field :first_release_last_commit, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Commit the error was first seen"
|
||||
description: 'Commit the error was first seen'
|
||||
field :last_release_last_commit, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Commit the error was last seen"
|
||||
description: 'Commit the error was last seen'
|
||||
field :first_release_short_version, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Release version the error was first seen"
|
||||
description: 'Release version the error was first seen'
|
||||
field :last_release_short_version, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Release version the error was last seen"
|
||||
description: 'Release version the error was last seen'
|
||||
field :gitlab_commit, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "GitLab commit SHA attributed to the Error based on the release version"
|
||||
description: 'GitLab commit SHA attributed to the Error based on the release version'
|
||||
field :gitlab_commit_path, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Path to the GitLab page for the GitLab commit attributed to the error"
|
||||
description: 'Path to the GitLab page for the GitLab commit attributed to the error'
|
||||
field :gitlab_issue_path, GraphQL::STRING_TYPE,
|
||||
method: :gitlab_issue,
|
||||
null: true,
|
||||
description: 'URL of GitLab Issue'
|
||||
field :tags, Types::ErrorTracking::SentryErrorTagsType,
|
||||
null: false,
|
||||
description: 'Tags associated with the Sentry Error'
|
||||
|
||||
def first_seen
|
||||
DateTime.parse(object.first_seen)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module ErrorTracking
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class SentryErrorTagsType < ::Types::BaseObject
|
||||
graphql_name 'SentryErrorTags'
|
||||
description 'State of a Sentry error'
|
||||
|
||||
field :level, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Severity level of the Sentry Error"
|
||||
field :logger, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: "Logger of the Sentry Error"
|
||||
end
|
||||
# rubocop: enable Graphql/AuthorizeTypes
|
||||
end
|
||||
end
|
|
@ -47,7 +47,7 @@ module BlobHelper
|
|||
def edit_blob_button(project = @project, ref = @ref, path = @path, options = {})
|
||||
return unless blob = readable_blob(options, path, project, ref)
|
||||
|
||||
common_classes = "btn btn-primary js-edit-blob #{options[:extra_class]}"
|
||||
common_classes = "btn btn-primary js-edit-blob ml-2 #{options[:extra_class]}"
|
||||
|
||||
edit_button_tag(blob,
|
||||
common_classes,
|
||||
|
@ -62,7 +62,7 @@ module BlobHelper
|
|||
return unless blob = readable_blob(options, path, project, ref)
|
||||
|
||||
edit_button_tag(blob,
|
||||
'btn btn-inverted btn-primary ide-edit-button',
|
||||
'btn btn-inverted btn-primary ide-edit-button ml-2',
|
||||
_('Web IDE'),
|
||||
ide_edit_path(project, ref, path, options),
|
||||
project,
|
||||
|
|
|
@ -4,6 +4,7 @@ module Clusters
|
|||
module Applications
|
||||
class Ingress < ApplicationRecord
|
||||
VERSION = '1.22.1'
|
||||
MODSECURITY_LOG_CONTAINER_NAME = 'modsecurity-log'
|
||||
|
||||
self.table_name = 'clusters_applications_ingress'
|
||||
|
||||
|
@ -85,7 +86,7 @@ module Clusters
|
|||
},
|
||||
"extraContainers" => [
|
||||
{
|
||||
"name" => "modsecurity-log",
|
||||
"name" => MODSECURITY_LOG_CONTAINER_NAME,
|
||||
"image" => "busybox",
|
||||
"args" => [
|
||||
"/bin/sh",
|
||||
|
|
|
@ -1938,6 +1938,8 @@ class Project < ApplicationRecord
|
|||
.append(key: 'GITLAB_CI', value: 'true')
|
||||
.append(key: 'CI_SERVER_URL', value: Gitlab.config.gitlab.url)
|
||||
.append(key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host)
|
||||
.append(key: 'CI_SERVER_PORT', value: Gitlab.config.gitlab.port.to_s)
|
||||
.append(key: 'CI_SERVER_PROTOCOL', value: Gitlab.config.gitlab.protocol)
|
||||
.append(key: 'CI_SERVER_NAME', value: 'GitLab')
|
||||
.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
|
||||
.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s)
|
||||
|
|
|
@ -2,18 +2,16 @@
|
|||
.js-file-title.file-title-flex-parent
|
||||
= render 'projects/blob/header_content', blob: blob
|
||||
|
||||
.file-actions
|
||||
.file-actions<
|
||||
= render 'projects/blob/viewer_switcher', blob: blob unless blame
|
||||
|
||||
.btn-group{ role: "group" }<
|
||||
= edit_blob_button
|
||||
= ide_edit_button
|
||||
.btn-group{ role: "group" }<
|
||||
= edit_blob_button
|
||||
= ide_edit_button
|
||||
.btn-group.ml-2{ role: "group" }>
|
||||
= render_if_exists 'projects/blob/header_file_locks_link'
|
||||
- if current_user
|
||||
= replace_blob_link
|
||||
= delete_blob_link
|
||||
.btn-group{ role: "group" }<
|
||||
.btn-group.ml-2{ role: "group" }
|
||||
= copy_blob_source_button(blob) unless blame
|
||||
= open_raw_blob_button(blob)
|
||||
= download_blob_button(blob)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
- simple_viewer = blob.simple_viewer
|
||||
- rich_viewer = blob.rich_viewer
|
||||
|
||||
.btn-group.js-blob-viewer-switcher{ role: "group" }
|
||||
.btn-group.js-blob-viewer-switcher.ml-2{ role: "group" }>
|
||||
- simple_label = "Display #{simple_viewer.switcher_title}"
|
||||
%button.btn.btn-default.btn-sm.js-blob-viewer-switch-btn.has-tooltip{ 'aria-label' => simple_label, title: simple_label, data: { viewer: 'simple', container: 'body' } }>
|
||||
= icon(simple_viewer.switcher_icon)
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
- if user == current_user
|
||||
%span.badge.badge-success.prepend-left-5= _("It's you")
|
||||
|
||||
= render_if_exists 'shared/members/ee/license_badge', user: user, group: @group
|
||||
|
||||
- if user.blocked?
|
||||
%label.badge.badge-danger
|
||||
%strong= _("Blocked")
|
||||
|
|
|
@ -6,6 +6,7 @@ class AuthorizedProjectsWorker
|
|||
|
||||
feature_category :authentication_and_authorization
|
||||
latency_sensitive_worker!
|
||||
weight 2
|
||||
|
||||
# This is a workaround for a Ruby 2.3.7 bug. rspec-mocks cannot restore the
|
||||
# visibility of prepended modules. See https://github.com/rspec/rspec-mocks/issues/1231
|
||||
|
|
|
@ -8,6 +8,8 @@ class ChatNotificationWorker
|
|||
sidekiq_options retry: false
|
||||
feature_category :chatops
|
||||
latency_sensitive_worker!
|
||||
weight 2
|
||||
|
||||
# TODO: break this into multiple jobs
|
||||
# as the `responder` uses external dependencies
|
||||
# See https://gitlab.com/gitlab-com/gl-infra/scalability/issues/34
|
||||
|
|
|
@ -9,6 +9,7 @@ module SelfMonitoringProjectWorker
|
|||
# Other Functionality. Metrics seems to be the closest feature_category for
|
||||
# this worker.
|
||||
feature_category :metrics
|
||||
weight 2
|
||||
end
|
||||
|
||||
LEASE_TIMEOUT = 15.minutes.to_i
|
||||
|
|
|
@ -7,6 +7,24 @@ module WorkerAttributes
|
|||
# `worker_resource_boundary` attribute
|
||||
VALID_RESOURCE_BOUNDARIES = [:memory, :cpu, :unknown].freeze
|
||||
|
||||
NAMESPACE_WEIGHTS = {
|
||||
auto_devops: 2,
|
||||
auto_merge: 3,
|
||||
chaos: 2,
|
||||
deployment: 3,
|
||||
mail_scheduler: 2,
|
||||
notifications: 2,
|
||||
pipeline_cache: 3,
|
||||
pipeline_creation: 4,
|
||||
pipeline_default: 3,
|
||||
pipeline_hooks: 2,
|
||||
pipeline_processing: 5,
|
||||
|
||||
# EE-specific
|
||||
epics: 2,
|
||||
incident_management: 2
|
||||
}.stringify_keys.freeze
|
||||
|
||||
class_methods do
|
||||
def feature_category(value)
|
||||
raise "Invalid category. Use `feature_category_not_owned!` to mark a worker as not owned" if value == :not_owned
|
||||
|
@ -70,6 +88,16 @@ module WorkerAttributes
|
|||
worker_attributes[:resource_boundary] || :unknown
|
||||
end
|
||||
|
||||
def weight(value)
|
||||
worker_attributes[:weight] = value
|
||||
end
|
||||
|
||||
def get_weight
|
||||
worker_attributes[:weight] ||
|
||||
NAMESPACE_WEIGHTS[queue_namespace] ||
|
||||
1
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Returns a worker attribute declared on this class or its parent class.
|
||||
|
|
|
@ -4,6 +4,7 @@ class CreateEvidenceWorker
|
|||
include ApplicationWorker
|
||||
|
||||
feature_category :release_governance
|
||||
weight 2
|
||||
|
||||
def perform(release_id)
|
||||
release = Release.find_by_id(release_id)
|
||||
|
|
|
@ -4,6 +4,7 @@ class CreateGpgSignatureWorker
|
|||
include ApplicationWorker
|
||||
|
||||
feature_category :source_code_management
|
||||
weight 2
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def perform(commit_shas, project_id)
|
||||
|
|
|
@ -5,6 +5,7 @@ class EmailReceiverWorker
|
|||
|
||||
feature_category :issue_tracking
|
||||
latency_sensitive_worker!
|
||||
weight 2
|
||||
|
||||
def perform(raw)
|
||||
return unless Gitlab::IncomingEmail.enabled?
|
||||
|
|
|
@ -8,6 +8,7 @@ class EmailsOnPushWorker
|
|||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
worker_resource_boundary :cpu
|
||||
weight 2
|
||||
|
||||
def perform(project_id, recipients, push_data, options = {})
|
||||
options.symbolize_keys!
|
||||
|
|
|
@ -6,6 +6,7 @@ class GitlabShellWorker
|
|||
|
||||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
weight 2
|
||||
|
||||
def perform(action, *arg)
|
||||
Gitlab::GitalyClient::NamespaceService.allow do
|
||||
|
|
|
@ -5,6 +5,7 @@ class ImportIssuesCsvWorker
|
|||
|
||||
feature_category :issue_tracking
|
||||
worker_resource_boundary :cpu
|
||||
weight 2
|
||||
|
||||
sidekiq_retries_exhausted do |job|
|
||||
Upload.find(job['args'][2]).destroy
|
||||
|
|
|
@ -4,6 +4,7 @@ class InvalidGpgSignatureUpdateWorker
|
|||
include ApplicationWorker
|
||||
|
||||
feature_category :source_code_management
|
||||
weight 2
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def perform(gpg_key_id)
|
||||
|
|
|
@ -5,6 +5,7 @@ class MergeWorker
|
|||
|
||||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
weight 5
|
||||
|
||||
def perform(merge_request_id, current_user_id, params)
|
||||
params = params.with_indifferent_access
|
||||
|
|
|
@ -7,6 +7,7 @@ class NewIssueWorker
|
|||
feature_category :issue_tracking
|
||||
latency_sensitive_worker!
|
||||
worker_resource_boundary :cpu
|
||||
weight 2
|
||||
|
||||
def perform(issue_id, user_id)
|
||||
return unless objects_found?(issue_id, user_id)
|
||||
|
|
|
@ -7,6 +7,7 @@ class NewMergeRequestWorker
|
|||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
worker_resource_boundary :cpu
|
||||
weight 2
|
||||
|
||||
def perform(merge_request_id, user_id)
|
||||
return unless objects_found?(merge_request_id, user_id)
|
||||
|
|
|
@ -6,6 +6,7 @@ class NewNoteWorker
|
|||
feature_category :issue_tracking
|
||||
latency_sensitive_worker!
|
||||
worker_resource_boundary :cpu
|
||||
weight 2
|
||||
|
||||
# Keep extra parameter to preserve backwards compatibility with
|
||||
# old `NewNoteWorker` jobs (can remove later)
|
||||
|
|
|
@ -5,6 +5,7 @@ class NewReleaseWorker
|
|||
|
||||
queue_namespace :notifications
|
||||
feature_category :release_orchestration
|
||||
weight 2
|
||||
|
||||
def perform(release_id)
|
||||
release = Release.preloaded.find_by_id(release_id)
|
||||
|
|
|
@ -6,6 +6,7 @@ class PostReceive
|
|||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
worker_resource_boundary :cpu
|
||||
weight 5
|
||||
|
||||
def perform(gl_repository, identifier, changes, push_options = {})
|
||||
project, repo_type = Gitlab::GlRepository.parse(gl_repository)
|
||||
|
|
|
@ -12,6 +12,7 @@ class ProcessCommitWorker
|
|||
|
||||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
weight 3
|
||||
|
||||
# project_id - The ID of the project this commit belongs to.
|
||||
# user_id - The ID of the user that pushed the commit.
|
||||
|
|
|
@ -6,6 +6,7 @@ class RebaseWorker
|
|||
include ApplicationWorker
|
||||
|
||||
feature_category :source_code_management
|
||||
weight 2
|
||||
|
||||
def perform(merge_request_id, current_user_id, skip_ci = false)
|
||||
current_user = User.find(current_user_id)
|
||||
|
|
|
@ -4,6 +4,7 @@ class RemoteMirrorNotificationWorker
|
|||
include ApplicationWorker
|
||||
|
||||
feature_category :source_code_management
|
||||
weight 2
|
||||
|
||||
def perform(remote_mirror_id)
|
||||
remote_mirror = RemoteMirror.find_by_id(remote_mirror_id)
|
||||
|
|
|
@ -4,6 +4,7 @@ class UpdateExternalPullRequestsWorker
|
|||
include ApplicationWorker
|
||||
|
||||
feature_category :source_code_management
|
||||
weight 3
|
||||
|
||||
def perform(project_id, user_id, ref)
|
||||
project = Project.find_by_id(project_id)
|
||||
|
|
|
@ -6,6 +6,7 @@ class UpdateMergeRequestsWorker
|
|||
feature_category :source_code_management
|
||||
latency_sensitive_worker!
|
||||
worker_resource_boundary :cpu
|
||||
weight 3
|
||||
|
||||
LOG_TIME_THRESHOLD = 90 # seconds
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add WAF Anomaly Summary service
|
||||
merge_request: 22736
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add tags, external_base_url, gitlab_issue to Sentry Detailed Error graphql
|
||||
merge_request: 23483
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove button group for edit and web ide in file header
|
||||
merge_request: 23291
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add CI variables to provide GitLab port and protocol
|
||||
merge_request: 23296
|
||||
author: Aidin Abedi
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow users to read broadcast messages via API
|
||||
merge_request: 23298
|
||||
author: Rajendra Kadam
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add accidentally deleted project config for custom apply suggestions
|
||||
merge_request: 23687
|
||||
author: Fabio Huser
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Support retrieval of disk statistics from Gitaly"
|
||||
merge_request: 22226
|
||||
author: Nels Nelson
|
||||
type: added
|
|
@ -15,6 +15,10 @@ unless Gitlab::Runtime.sidekiq?
|
|||
data
|
||||
end
|
||||
|
||||
# This isn't a user-reachable controller; we use it to check for a
|
||||
# valid CSRF token in the API
|
||||
config.lograge.ignore_actions = ['Gitlab::RequestForgeryProtection::Controller#index']
|
||||
|
||||
# Add request parameters to log output
|
||||
config.lograge.custom_options = lambda do |event|
|
||||
params = event.payload[:params]
|
||||
|
|
|
@ -1,43 +1,51 @@
|
|||
scope(path: '*namespace_id/:project_id',
|
||||
concern :gitactionable do
|
||||
scope(controller: :git_http) do
|
||||
get '/info/refs', action: :info_refs
|
||||
post '/git-upload-pack', action: :git_upload_pack
|
||||
post '/git-receive-pack', action: :git_receive_pack
|
||||
end
|
||||
end
|
||||
|
||||
concern :lfsable do
|
||||
# Git LFS API (metadata)
|
||||
scope(path: 'info/lfs/objects', controller: :lfs_api) do
|
||||
post :batch
|
||||
post '/', action: :deprecated
|
||||
get '/*oid', action: :deprecated
|
||||
end
|
||||
|
||||
scope(path: 'info/lfs') do
|
||||
resources :lfs_locks, controller: :lfs_locks_api, path: 'locks' do
|
||||
post :unlock, on: :member
|
||||
post :verify, on: :collection
|
||||
end
|
||||
end
|
||||
|
||||
# GitLab LFS object storage
|
||||
scope(path: 'gitlab-lfs/objects/*oid', controller: :lfs_storage, constraints: { oid: /[a-f0-9]{64}/ }) do
|
||||
get '/', action: :download
|
||||
|
||||
scope constraints: { size: /[0-9]+/ } do
|
||||
put '/*size/authorize', action: :upload_authorize
|
||||
put '/*size', action: :upload_finalize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scope(path: '*namespace_id/:repository_id',
|
||||
format: nil,
|
||||
constraints: { namespace_id: Gitlab::PathRegex.full_namespace_route_regex }) do
|
||||
scope(constraints: { project_id: Gitlab::PathRegex.project_git_route_regex }, module: :projects) do
|
||||
# Git HTTP clients ('git clone' etc.)
|
||||
scope(controller: :git_http) do
|
||||
get '/info/refs', action: :info_refs
|
||||
post '/git-upload-pack', action: :git_upload_pack
|
||||
post '/git-receive-pack', action: :git_receive_pack
|
||||
end
|
||||
|
||||
# Git LFS API (metadata)
|
||||
scope(path: 'info/lfs/objects', controller: :lfs_api) do
|
||||
post :batch
|
||||
post '/', action: :deprecated
|
||||
get '/*oid', action: :deprecated
|
||||
end
|
||||
|
||||
scope(path: 'info/lfs') do
|
||||
resources :lfs_locks, controller: :lfs_locks_api, path: 'locks' do
|
||||
post :unlock, on: :member
|
||||
post :verify, on: :collection
|
||||
end
|
||||
end
|
||||
|
||||
# GitLab LFS object storage
|
||||
scope(path: 'gitlab-lfs/objects/*oid', controller: :lfs_storage, constraints: { oid: /[a-f0-9]{64}/ }) do
|
||||
get '/', action: :download
|
||||
|
||||
scope constraints: { size: /[0-9]+/ } do
|
||||
put '/*size/authorize', action: :upload_authorize
|
||||
put '/*size', action: :upload_finalize
|
||||
end
|
||||
scope(constraints: { repository_id: Gitlab::PathRegex.project_git_route_regex }) do
|
||||
scope(module: :repositories) do
|
||||
concerns :gitactionable
|
||||
concerns :lfsable
|
||||
end
|
||||
end
|
||||
|
||||
# Redirect /group/project.wiki.git to the project wiki
|
||||
scope(format: true, constraints: { project_id: Gitlab::PathRegex.project_wiki_git_route_regex, format: :git }) do
|
||||
scope(format: true, constraints: { repository_id: Gitlab::PathRegex.project_wiki_git_route_regex, format: :git }) do
|
||||
wiki_redirect = redirect do |params, request|
|
||||
project_id = params[:project_id].delete_suffix('.wiki')
|
||||
project_id = params[:repository_id].delete_suffix('.wiki')
|
||||
path = [params[:namespace_id], project_id, 'wikis'].join('/')
|
||||
path << "?#{request.query_string}" unless request.query_string.blank?
|
||||
path
|
||||
|
@ -47,7 +55,7 @@ scope(path: '*namespace_id/:project_id',
|
|||
end
|
||||
|
||||
# Redirect /group/project/info/refs to /group/project.git/info/refs
|
||||
scope(constraints: { project_id: Gitlab::PathRegex.project_route_regex }) do
|
||||
scope(constraints: { repository_id: Gitlab::PathRegex.project_route_regex }) do
|
||||
# Allow /info/refs, /info/refs?service=git-upload-pack, and
|
||||
# /info/refs?service=git-receive-pack, but nothing else.
|
||||
#
|
||||
|
@ -58,7 +66,7 @@ scope(path: '*namespace_id/:project_id',
|
|||
end
|
||||
|
||||
ref_redirect = redirect do |params, request|
|
||||
path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
|
||||
path = "#{params[:namespace_id]}/#{params[:repository_id]}.git/info/refs"
|
||||
path << "?#{request.query_string}" unless request.query_string.blank?
|
||||
path
|
||||
end
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
# This file is generated automatically by
|
||||
# bin/rake gitlab:sidekiq:sidekiq_queues_yml:generate
|
||||
#
|
||||
# Do not edit it manually!
|
||||
#
|
||||
# This configuration file should be exclusively used to set queue settings for
|
||||
# Sidekiq. Any other setting should be specified using the Sidekiq CLI or the
|
||||
# Sidekiq Ruby API (see config/initializers/sidekiq.rb).
|
||||
---
|
||||
#
|
||||
# All the queues to process and their weights. Every queue _must_ have a weight
|
||||
# defined.
|
||||
#
|
||||
|
@ -17,116 +22,217 @@
|
|||
# to perform) is:
|
||||
#
|
||||
# chance = (queue weight / total weight of all queues) * 100
|
||||
---
|
||||
:queues:
|
||||
- [post_receive, 5]
|
||||
- [merge, 5]
|
||||
- [update_merge_requests, 3]
|
||||
- [process_commit, 3]
|
||||
- [new_note, 2]
|
||||
- [new_issue, 2]
|
||||
- [notifications, 2]
|
||||
- [new_merge_request, 2]
|
||||
- [pipeline_processing, 5]
|
||||
- [pipeline_creation, 4]
|
||||
- [pipeline_default, 3]
|
||||
- [pipeline_cache, 3]
|
||||
- [deployment, 3]
|
||||
- [auto_merge, 3]
|
||||
- [pipeline_hooks, 2]
|
||||
- [gitlab_shell, 2]
|
||||
- [email_receiver, 2]
|
||||
- [emails_on_push, 2]
|
||||
- [mailers, 2]
|
||||
- [mail_scheduler, 2]
|
||||
- [invalid_gpg_signature_update, 2]
|
||||
- [create_gpg_signature, 2]
|
||||
- [rebase, 2]
|
||||
- [upload_checksum, 1]
|
||||
- [repository_fork, 1]
|
||||
- [repository_import, 1]
|
||||
- [github_importer, 1]
|
||||
- [github_import_advance_stage, 1]
|
||||
- [project_service, 1]
|
||||
- [delete_user, 1]
|
||||
- [todos_destroyer, 1]
|
||||
- [delete_merged_branches, 1]
|
||||
- [authorized_projects, 2]
|
||||
- [expire_build_instance_artifacts, 1]
|
||||
- [group_destroy, 1]
|
||||
- [irker, 1]
|
||||
- [namespaceless_project_destroy, 1]
|
||||
- [project_cache, 1]
|
||||
- [project_destroy, 1]
|
||||
- [project_export, 1]
|
||||
- [web_hook, 1]
|
||||
- [repository_check, 1]
|
||||
- [git_garbage_collect, 1]
|
||||
- [reactive_caching, 1]
|
||||
- [cronjob, 1]
|
||||
- [default, 1]
|
||||
- [pages, 1]
|
||||
- [system_hook_push, 1]
|
||||
- [propagate_service_template, 1]
|
||||
- [background_migration, 1]
|
||||
- [gcp_cluster, 1]
|
||||
- [project_migrate_hashed_storage, 1]
|
||||
- [project_rollback_hashed_storage, 1]
|
||||
- [hashed_storage, 1]
|
||||
- [pages_domain_verification, 1]
|
||||
- [pages_domain_ssl_renewal, 1]
|
||||
- [object_storage_upload, 1]
|
||||
- [object_storage, 1]
|
||||
- [file_hook, 1]
|
||||
- [pipeline_background, 1]
|
||||
- [repository_update_remote_mirror, 1]
|
||||
- [repository_remove_remote, 1]
|
||||
- [create_note_diff_file, 1]
|
||||
- [delete_diff_files, 1]
|
||||
- [detect_repository_languages, 1]
|
||||
- [auto_devops, 2]
|
||||
- [container_repository, 1]
|
||||
- [object_pool, 1]
|
||||
- [repository_cleanup, 1]
|
||||
- [delete_stored_files, 1]
|
||||
- [remote_mirror_notification, 2]
|
||||
- [project_daily_statistics, 1]
|
||||
- [import_issues_csv, 2]
|
||||
- [chat_notification, 2]
|
||||
- [migrate_external_diffs, 1]
|
||||
- [update_project_statistics, 1]
|
||||
- [phabricator_import_import_tasks, 1]
|
||||
- [update_namespace_statistics, 1]
|
||||
- [chaos, 2]
|
||||
- [create_evidence, 2]
|
||||
- [group_export, 1]
|
||||
- [self_monitoring_project_create, 2]
|
||||
- [self_monitoring_project_delete, 2]
|
||||
- [error_tracking_issue_link, 2]
|
||||
- [merge_request_mergeability_check, 5]
|
||||
|
||||
# EE-specific queues
|
||||
- [analytics, 1]
|
||||
- [ldap_group_sync, 2]
|
||||
- [create_github_webhook, 2]
|
||||
- [geo, 1]
|
||||
- [repository_update_mirror, 1]
|
||||
- [repository_push_audit_event, 1]
|
||||
- [new_epic, 2]
|
||||
- [project_import_schedule, 1]
|
||||
- [project_update_repository_storage, 1]
|
||||
- [admin_emails, 1]
|
||||
- [elastic_batch_project_indexer, 1]
|
||||
- [elastic_indexer, 1]
|
||||
- [elastic_full_index, 1]
|
||||
- [elastic_commit_indexer, 1]
|
||||
- [elastic_namespace_indexer, 1]
|
||||
- [elastic_namespace_rollout, 1]
|
||||
- [export_csv, 1]
|
||||
- [incident_management, 2]
|
||||
- [jira_connect, 1]
|
||||
- [update_external_pull_requests, 3]
|
||||
- [refresh_license_compliance_checks, 2]
|
||||
- [design_management_new_version, 1]
|
||||
- [epics, 2]
|
||||
- [personal_access_tokens, 1]
|
||||
- [adjourned_project_deletion, 1]
|
||||
- - adjourned_project_deletion
|
||||
- 1
|
||||
- - admin_emails
|
||||
- 1
|
||||
- - authorized_projects
|
||||
- 2
|
||||
- - auto_devops
|
||||
- 2
|
||||
- - auto_merge
|
||||
- 3
|
||||
- - background_migration
|
||||
- 1
|
||||
- - chaos
|
||||
- 2
|
||||
- - chat_notification
|
||||
- 2
|
||||
- - container_repository
|
||||
- 1
|
||||
- - create_evidence
|
||||
- 2
|
||||
- - create_github_webhook
|
||||
- 2
|
||||
- - create_gpg_signature
|
||||
- 2
|
||||
- - create_note_diff_file
|
||||
- 1
|
||||
- - cronjob
|
||||
- 1
|
||||
- - default
|
||||
- 1
|
||||
- - delete_diff_files
|
||||
- 1
|
||||
- - delete_merged_branches
|
||||
- 1
|
||||
- - delete_stored_files
|
||||
- 1
|
||||
- - delete_user
|
||||
- 1
|
||||
- - deployment
|
||||
- 3
|
||||
- - design_management_new_version
|
||||
- 1
|
||||
- - detect_repository_languages
|
||||
- 1
|
||||
- - elastic_batch_project_indexer
|
||||
- 1
|
||||
- - elastic_commit_indexer
|
||||
- 1
|
||||
- - elastic_full_index
|
||||
- 1
|
||||
- - elastic_indexer
|
||||
- 1
|
||||
- - elastic_namespace_indexer
|
||||
- 1
|
||||
- - elastic_namespace_rollout
|
||||
- 1
|
||||
- - email_receiver
|
||||
- 2
|
||||
- - emails_on_push
|
||||
- 2
|
||||
- - epics
|
||||
- 2
|
||||
- - error_tracking_issue_link
|
||||
- 1
|
||||
- - expire_build_instance_artifacts
|
||||
- 1
|
||||
- - export_csv
|
||||
- 1
|
||||
- - file_hook
|
||||
- 1
|
||||
- - gcp_cluster
|
||||
- 1
|
||||
- - geo
|
||||
- 1
|
||||
- - git_garbage_collect
|
||||
- 1
|
||||
- - github_import_advance_stage
|
||||
- 1
|
||||
- - github_importer
|
||||
- 1
|
||||
- - gitlab_shell
|
||||
- 2
|
||||
- - group_destroy
|
||||
- 1
|
||||
- - group_export
|
||||
- 1
|
||||
- - hashed_storage
|
||||
- 1
|
||||
- - import_issues_csv
|
||||
- 2
|
||||
- - incident_management
|
||||
- 2
|
||||
- - invalid_gpg_signature_update
|
||||
- 2
|
||||
- - irker
|
||||
- 1
|
||||
- - jira_connect
|
||||
- 1
|
||||
- - ldap_group_sync
|
||||
- 2
|
||||
- - mail_scheduler
|
||||
- 2
|
||||
- - mailers
|
||||
- 2
|
||||
- - merge
|
||||
- 5
|
||||
- - merge_request_mergeability_check
|
||||
- 1
|
||||
- - migrate_external_diffs
|
||||
- 1
|
||||
- - namespaceless_project_destroy
|
||||
- 1
|
||||
- - new_epic
|
||||
- 2
|
||||
- - new_issue
|
||||
- 2
|
||||
- - new_merge_request
|
||||
- 2
|
||||
- - new_note
|
||||
- 2
|
||||
- - notifications
|
||||
- 2
|
||||
- - object_pool
|
||||
- 1
|
||||
- - object_storage
|
||||
- 1
|
||||
- - pages
|
||||
- 1
|
||||
- - pages_domain_ssl_renewal
|
||||
- 1
|
||||
- - pages_domain_verification
|
||||
- 1
|
||||
- - personal_access_tokens
|
||||
- 1
|
||||
- - phabricator_import_import_tasks
|
||||
- 1
|
||||
- - pipeline_background
|
||||
- 1
|
||||
- - pipeline_cache
|
||||
- 3
|
||||
- - pipeline_creation
|
||||
- 4
|
||||
- - pipeline_default
|
||||
- 3
|
||||
- - pipeline_hooks
|
||||
- 2
|
||||
- - pipeline_processing
|
||||
- 5
|
||||
- - post_receive
|
||||
- 5
|
||||
- - process_commit
|
||||
- 3
|
||||
- - project_cache
|
||||
- 1
|
||||
- - project_daily_statistics
|
||||
- 1
|
||||
- - project_destroy
|
||||
- 1
|
||||
- - project_export
|
||||
- 1
|
||||
- - project_import_schedule
|
||||
- 1
|
||||
- - project_service
|
||||
- 1
|
||||
- - project_update_repository_storage
|
||||
- 1
|
||||
- - propagate_service_template
|
||||
- 1
|
||||
- - reactive_caching
|
||||
- 1
|
||||
- - rebase
|
||||
- 2
|
||||
- - refresh_license_compliance_checks
|
||||
- 2
|
||||
- - remote_mirror_notification
|
||||
- 2
|
||||
- - repository_check
|
||||
- 1
|
||||
- - repository_cleanup
|
||||
- 1
|
||||
- - repository_fork
|
||||
- 1
|
||||
- - repository_import
|
||||
- 1
|
||||
- - repository_push_audit_event
|
||||
- 1
|
||||
- - repository_remove_remote
|
||||
- 1
|
||||
- - repository_update_mirror
|
||||
- 1
|
||||
- - repository_update_remote_mirror
|
||||
- 1
|
||||
- - self_monitoring_project_create
|
||||
- 2
|
||||
- - self_monitoring_project_delete
|
||||
- 2
|
||||
- - system_hook_push
|
||||
- 1
|
||||
- - todos_destroyer
|
||||
- 1
|
||||
- - update_external_pull_requests
|
||||
- 3
|
||||
- - update_merge_requests
|
||||
- 3
|
||||
- - update_namespace_statistics
|
||||
- 1
|
||||
- - update_project_statistics
|
||||
- 1
|
||||
- - upload_checksum
|
||||
- 1
|
||||
- - web_hook
|
||||
- 1
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddSourceToImportFailures < ActiveRecord::Migration[5.2]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :import_failures, :source, :string, limit: 128
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_01_23_155929) do
|
||||
ActiveRecord::Schema.define(version: 2020_01_24_053531) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_trgm"
|
||||
|
@ -2046,6 +2046,7 @@ ActiveRecord::Schema.define(version: 2020_01_23_155929) do
|
|||
t.string "exception_message", limit: 255
|
||||
t.integer "retry_count"
|
||||
t.integer "group_id"
|
||||
t.string "source", limit: 128
|
||||
t.index ["correlation_id_value"], name: "index_import_failures_on_correlation_id_value"
|
||||
t.index ["group_id"], name: "index_import_failures_on_group_id_not_null", where: "(group_id IS NOT NULL)"
|
||||
t.index ["project_id"], name: "index_import_failures_on_project_id_not_null", where: "(project_id IS NOT NULL)"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Broadcast messages API operates on [broadcast messages](../user/admin_area/broadcast_messages.md).
|
||||
|
||||
The broadcast message API is only accessible to administrators. All requests by:
|
||||
As of GitLab 12.8, GET requests do not require authentication. All other broadcast message API endpoints are accessible only to administrators. Non-GET requests by:
|
||||
|
||||
- Guests will result in `401 Unauthorized`.
|
||||
- Regular users will result in `403 Forbidden`.
|
||||
|
@ -20,7 +20,7 @@ GET /broadcast_messages
|
|||
Example request:
|
||||
|
||||
```sh
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/broadcast_messages
|
||||
curl https://gitlab.example.com/api/v4/broadcast_messages
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -57,7 +57,7 @@ Parameters:
|
|||
Example request:
|
||||
|
||||
```sh
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/broadcast_messages/1
|
||||
curl https://gitlab.example.com/api/v4/broadcast_messages/1
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
|
|
@ -6065,6 +6065,11 @@ type SentryDetailedError {
|
|||
"""
|
||||
culprit: String!
|
||||
|
||||
"""
|
||||
External Base URL of the Sentry Instance
|
||||
"""
|
||||
externalBaseUrl: String!
|
||||
|
||||
"""
|
||||
External URL of the error
|
||||
"""
|
||||
|
@ -6100,6 +6105,11 @@ type SentryDetailedError {
|
|||
"""
|
||||
gitlabCommitPath: String
|
||||
|
||||
"""
|
||||
URL of GitLab Issue
|
||||
"""
|
||||
gitlabIssuePath: String
|
||||
|
||||
"""
|
||||
ID (global ID) of the error
|
||||
"""
|
||||
|
@ -6155,6 +6165,11 @@ type SentryDetailedError {
|
|||
"""
|
||||
status: SentryErrorStatus!
|
||||
|
||||
"""
|
||||
Tags associated with the Sentry Error
|
||||
"""
|
||||
tags: SentryErrorTags!
|
||||
|
||||
"""
|
||||
Title of the error
|
||||
"""
|
||||
|
@ -6208,6 +6223,21 @@ enum SentryErrorStatus {
|
|||
UNRESOLVED
|
||||
}
|
||||
|
||||
"""
|
||||
State of a Sentry error
|
||||
"""
|
||||
type SentryErrorTags {
|
||||
"""
|
||||
Severity level of the Sentry Error
|
||||
"""
|
||||
level: String
|
||||
|
||||
"""
|
||||
Logger of the Sentry Error
|
||||
"""
|
||||
logger: String
|
||||
}
|
||||
|
||||
"""
|
||||
Represents a snippet entry
|
||||
"""
|
||||
|
|
|
@ -16746,6 +16746,24 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "externalBaseUrl",
|
||||
"description": "External Base URL of the Sentry Instance",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "externalUrl",
|
||||
"description": "External URL of the error",
|
||||
|
@ -16864,6 +16882,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "gitlabIssuePath",
|
||||
"description": "URL of GitLab Issue",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "ID (global ID) of the error",
|
||||
|
@ -17050,6 +17082,24 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"description": "Tags associated with the Sentry Error",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "SentryErrorTags",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"description": "Title of the error",
|
||||
|
@ -17196,6 +17246,47 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "SentryErrorTags",
|
||||
"description": "State of a Sentry error",
|
||||
"fields": [
|
||||
{
|
||||
"name": "level",
|
||||
"description": "Severity level of the Sentry Error",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "logger",
|
||||
"description": "Logger of the Sentry Error",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "GrafanaIntegration",
|
||||
|
|
|
@ -923,6 +923,7 @@ Autogenerated return type of RemoveAwardEmoji
|
|||
| --- | ---- | ---------- |
|
||||
| `count` | Int! | Count of occurrences |
|
||||
| `culprit` | String! | Culprit of the error |
|
||||
| `externalBaseUrl` | String! | External Base URL of the Sentry Instance |
|
||||
| `externalUrl` | String! | External URL of the error |
|
||||
| `firstReleaseLastCommit` | String | Commit the error was first seen |
|
||||
| `firstReleaseShortVersion` | String | Release version the error was first seen |
|
||||
|
@ -930,6 +931,7 @@ Autogenerated return type of RemoveAwardEmoji
|
|||
| `frequency` | SentryErrorFrequency! => Array | Last 24hr stats of the error |
|
||||
| `gitlabCommit` | String | GitLab commit SHA attributed to the Error based on the release version |
|
||||
| `gitlabCommitPath` | String | Path to the GitLab page for the GitLab commit attributed to the error |
|
||||
| `gitlabIssuePath` | String | URL of GitLab Issue |
|
||||
| `id` | ID! | ID (global ID) of the error |
|
||||
| `lastReleaseLastCommit` | String | Commit the error was last seen |
|
||||
| `lastReleaseShortVersion` | String | Release version the error was last seen |
|
||||
|
@ -941,6 +943,7 @@ Autogenerated return type of RemoveAwardEmoji
|
|||
| `sentryProjectSlug` | String! | Slug of the project affected by the error |
|
||||
| `shortId` | String! | Short ID (Sentry ID) of the error |
|
||||
| `status` | SentryErrorStatus! | Status of the error |
|
||||
| `tags` | SentryErrorTags! | Tags associated with the Sentry Error |
|
||||
| `title` | String! | Title of the error |
|
||||
| `type` | String! | Type of the error |
|
||||
| `userCount` | Int! | Count of users affected by the error |
|
||||
|
@ -952,6 +955,15 @@ Autogenerated return type of RemoveAwardEmoji
|
|||
| `count` | Int! | Count of errors received since the previously recorded time |
|
||||
| `time` | Time! | Time the error frequency stats were recorded |
|
||||
|
||||
## SentryErrorTags
|
||||
|
||||
State of a Sentry error
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `level` | String | Severity level of the Sentry Error |
|
||||
| `logger` | String | Logger of the Sentry Error |
|
||||
|
||||
## Snippet
|
||||
|
||||
Represents a snippet entry
|
||||
|
|
|
@ -292,6 +292,8 @@ export CI_RUNNER_TAGS="docker, linux"
|
|||
export CI_SERVER="yes"
|
||||
export CI_SERVER_URL="https://example.com"
|
||||
export CI_SERVER_HOST="example.com"
|
||||
export CI_SERVER_PORT="443"
|
||||
export CI_SERVER_PROTOCOL="https"
|
||||
export CI_SERVER_NAME="GitLab"
|
||||
export CI_SERVER_REVISION="70606bf"
|
||||
export CI_SERVER_VERSION="8.9.0"
|
||||
|
@ -686,6 +688,10 @@ if [[ -d "/builds/gitlab-examples/ci-debug-trace/.git" ]]; then
|
|||
++ CI_SERVER_URL=https://gitlab.com:3000
|
||||
++ export CI_SERVER_HOST=gitlab.com
|
||||
++ CI_SERVER_HOST=gitlab.com
|
||||
++ export CI_SERVER_PORT=3000
|
||||
++ CI_SERVER_PORT=3000
|
||||
++ export CI_SERVER_PROTOCOL=https
|
||||
++ CI_SERVER_PROTOCOL=https
|
||||
++ export CI_SERVER_NAME=GitLab
|
||||
++ CI_SERVER_NAME=GitLab
|
||||
++ export CI_SERVER_VERSION=12.6.0-pre
|
||||
|
|
|
@ -114,6 +114,8 @@ future GitLab releases.**
|
|||
| `CI_SERVER` | all | all | Mark that job is executed in CI environment |
|
||||
| `CI_SERVER_URL` | 12.7 | all | The base URL of the GitLab instance, including protocol and port (like `https://gitlab.example.com:8080`) |
|
||||
| `CI_SERVER_HOST` | 12.1 | all | Host component of the GitLab instance URL, without protocol and port (like `gitlab.example.com`) |
|
||||
| `CI_SERVER_PORT` | 12.8 | all | Port component of the GitLab instance URL, without host and protocol (like `3000`) |
|
||||
| `CI_SERVER_PROTOCOL` | 12.8 | all | Protocol component of the GitLab instance URL, without host and port (like `https`) |
|
||||
| `CI_SERVER_NAME` | all | all | The name of CI server that is used to coordinate jobs |
|
||||
| `CI_SERVER_REVISION` | all | all | GitLab revision that is used to schedule jobs |
|
||||
| `CI_SERVER_VERSION` | all | all | GitLab version that is used to schedule jobs |
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
|
@ -439,6 +439,16 @@ Suggestions covering multiple lines are limited to 100 lines _above_ and 100
|
|||
lines _below_ the commented diff line, allowing up to 200 changed lines per
|
||||
suggestion.
|
||||
|
||||
### Code block nested in Suggestions
|
||||
|
||||
If you need to make a suggestion that involves a
|
||||
[fenced code block](../markdown.md#code-spans-and-blocks), wrap your suggestion in four backticks
|
||||
instead of the usual three.
|
||||
|
||||
![A comment editor with a suggestion with a fenced code block](img/suggestion_code_block_editor_v12_8.png)
|
||||
|
||||
![Ouput of a comment with a suggestion with a fenced code block](img/suggestion_code_block_output_v12_8.png)
|
||||
|
||||
### Configure the commit message for applied Suggestions
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/13086) in GitLab 12.7.
|
||||
|
|
|
@ -4,9 +4,6 @@ module API
|
|||
class BroadcastMessages < Grape::API
|
||||
include PaginationParams
|
||||
|
||||
before { authenticate! }
|
||||
before { authenticated_as_admin! }
|
||||
|
||||
resource :broadcast_messages do
|
||||
helpers do
|
||||
def find_message
|
||||
|
@ -40,6 +37,8 @@ module API
|
|||
optional :target_path, type: String, desc: 'Target path'
|
||||
end
|
||||
post do
|
||||
authenticated_as_admin!
|
||||
|
||||
message = BroadcastMessage.create(declared_params(include_missing: false))
|
||||
|
||||
if message.persisted?
|
||||
|
@ -76,6 +75,8 @@ module API
|
|||
optional :target_path, type: String, desc: 'Target path'
|
||||
end
|
||||
put ':id' do
|
||||
authenticated_as_admin!
|
||||
|
||||
message = find_message
|
||||
|
||||
if message.update(declared_params(include_missing: false))
|
||||
|
@ -93,6 +94,8 @@ module API
|
|||
requires :id, type: Integer, desc: 'Broadcast message ID'
|
||||
end
|
||||
delete ':id' do
|
||||
authenticated_as_admin!
|
||||
|
||||
message = find_message
|
||||
|
||||
destroy_conditionally!(message)
|
||||
|
|
|
@ -104,14 +104,12 @@ module API
|
|||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
def set_project
|
||||
if params[:gl_repository]
|
||||
@project, @repo_type = Gitlab::GlRepository.parse(params[:gl_repository])
|
||||
@redirected_path = nil
|
||||
elsif params[:project]
|
||||
@project, @repo_type, @redirected_path = Gitlab::RepoPath.parse(params[:project])
|
||||
else
|
||||
@project, @repo_type, @redirected_path = nil, nil, nil
|
||||
end
|
||||
@project, @repo_type, @redirected_path =
|
||||
if params[:gl_repository]
|
||||
Gitlab::GlRepository.parse(params[:gl_repository])
|
||||
elsif params[:project]
|
||||
Gitlab::RepoPath.parse(params[:project])
|
||||
end
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module Constraints
|
|||
class ProjectUrlConstrainer
|
||||
def matches?(request, existence_check: true)
|
||||
namespace_path = request.params[:namespace_id]
|
||||
project_path = request.params[:project_id] || request.params[:id]
|
||||
project_path = request.params[:project_id] || request.params[:id] || request.params[:repository_id]
|
||||
full_path = [namespace_path, project_path].join('/')
|
||||
|
||||
return false unless ProjectPathValidator.valid_path?(full_path)
|
||||
|
|
|
@ -9,7 +9,6 @@ class Feature
|
|||
%w[
|
||||
cache_invalidator
|
||||
inforef_uploadpack_cache
|
||||
get_tag_messages_go
|
||||
filter_shas_with_signatures_go
|
||||
commit_without_batch_check
|
||||
].freeze
|
||||
|
|
|
@ -53,6 +53,20 @@ module Gitaly
|
|||
storage_status&.fs_type
|
||||
end
|
||||
|
||||
def disk_used
|
||||
disk_statistics_storage_status&.used
|
||||
end
|
||||
|
||||
def disk_available
|
||||
disk_statistics_storage_status&.available
|
||||
end
|
||||
|
||||
# Simple convenience method for when obtaining both used and available
|
||||
# statistics at once is preferred.
|
||||
def disk_stats
|
||||
disk_statistics_storage_status
|
||||
end
|
||||
|
||||
def address
|
||||
Gitlab::GitalyClient.address(@storage)
|
||||
rescue RuntimeError => e
|
||||
|
@ -65,6 +79,10 @@ module Gitaly
|
|||
@storage_status ||= info.storage_statuses.find { |s| s.storage_name == storage }
|
||||
end
|
||||
|
||||
def disk_statistics_storage_status
|
||||
@disk_statistics_storage_status ||= disk_statistics.storage_statuses.find { |s| s.storage_name == storage }
|
||||
end
|
||||
|
||||
def matches_sha?
|
||||
match = server_version.match(SHA_VERSION_REGEX)
|
||||
return false unless match
|
||||
|
@ -76,7 +94,19 @@ module Gitaly
|
|||
@info ||=
|
||||
begin
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).info
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
|
||||
Gitlab::ErrorTracking.track_exception(ex)
|
||||
# This will show the server as being out of date
|
||||
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
|
||||
end
|
||||
end
|
||||
|
||||
def disk_statistics
|
||||
@disk_statistics ||=
|
||||
begin
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).disk_statistics
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
|
||||
Gitlab::ErrorTracking.track_exception(ex)
|
||||
# This will show the server as being out of date
|
||||
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
|
||||
end
|
||||
|
|
|
@ -432,10 +432,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def self.filesystem_id(storage)
|
||||
response = Gitlab::GitalyClient::ServerService.new(storage).info
|
||||
storage_status = response.storage_statuses.find { |status| status.storage_name == storage }
|
||||
|
||||
storage_status&.filesystem_id
|
||||
Gitlab::GitalyClient::ServerService.new(storage).storage_info&.filesystem_id
|
||||
end
|
||||
|
||||
def self.filesystem_id_from_disk(storage)
|
||||
|
@ -446,6 +443,14 @@ module Gitlab
|
|||
nil
|
||||
end
|
||||
|
||||
def self.filesystem_disk_available(storage)
|
||||
Gitlab::GitalyClient::ServerService.new(storage).storage_disk_statistics&.available
|
||||
end
|
||||
|
||||
def self.filesystem_disk_used(storage)
|
||||
Gitlab::GitalyClient::ServerService.new(storage).storage_disk_statistics&.used
|
||||
end
|
||||
|
||||
def self.timeout(timeout_name)
|
||||
Gitlab::CurrentSettings.current_application_settings[timeout_name]
|
||||
end
|
||||
|
|
|
@ -13,6 +13,24 @@ module Gitlab
|
|||
def info
|
||||
GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new, timeout: GitalyClient.fast_timeout)
|
||||
end
|
||||
|
||||
def disk_statistics
|
||||
GitalyClient.call(@storage, :server_service, :disk_statistics, Gitaly::DiskStatisticsRequest.new, timeout: GitalyClient.fast_timeout)
|
||||
end
|
||||
|
||||
def storage_info
|
||||
storage_specific(info)
|
||||
end
|
||||
|
||||
def storage_disk_statistics
|
||||
storage_specific(disk_statistics)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage_specific(response)
|
||||
response.storage_statuses.find { |status| status.storage_name == @storage }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,6 +43,9 @@ module Gitlab
|
|||
# Initialize gon.features with any flags that should be
|
||||
# made globally available to the frontend
|
||||
push_frontend_feature_flag(:snippets_vue, default_enabled: false)
|
||||
push_frontend_feature_flag(:monaco_snippets, default_enabled: false)
|
||||
push_frontend_feature_flag(:monaco_blobs, default_enabled: false)
|
||||
push_frontend_feature_flag(:monaco_ci, default_enabled: false)
|
||||
end
|
||||
|
||||
# Exposes the state of a feature flag to the frontend code.
|
||||
|
|
|
@ -12,9 +12,14 @@ module Gitlab
|
|||
@association = importable.association(:import_failures)
|
||||
end
|
||||
|
||||
def with_retry(relation_key, relation_index)
|
||||
def with_retry(action:, relation_key: nil, relation_index: nil)
|
||||
on_retry = -> (exception, retry_count, *_args) do
|
||||
log_import_failure(relation_key, relation_index, exception, retry_count)
|
||||
log_import_failure(
|
||||
source: action,
|
||||
relation_key: relation_key,
|
||||
relation_index: relation_index,
|
||||
exception: exception,
|
||||
retry_count: retry_count)
|
||||
end
|
||||
|
||||
Retriable.with_context(:relation_import, on_retry: on_retry) do
|
||||
|
@ -22,8 +27,9 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def log_import_failure(relation_key, relation_index, exception, retry_count = 0)
|
||||
def log_import_failure(source:, relation_key: nil, relation_index: nil, exception:, retry_count: 0)
|
||||
extra = {
|
||||
source: source,
|
||||
relation_key: relation_key,
|
||||
relation_index: relation_index,
|
||||
retry_count: retry_count
|
||||
|
|
|
@ -21,7 +21,9 @@ module Gitlab
|
|||
RelationRenameService.rename(@tree_hash)
|
||||
|
||||
if relation_tree_restorer.restore
|
||||
@project.merge_requests.set_latest_merge_request_diff_ids!
|
||||
import_failure_service.with_retry(action: 'set_latest_merge_request_diff_ids!') do
|
||||
@project.merge_requests.set_latest_merge_request_diff_ids!
|
||||
end
|
||||
|
||||
true
|
||||
else
|
||||
|
@ -72,6 +74,10 @@ module Gitlab
|
|||
def reader
|
||||
@reader ||= Gitlab::ImportExport::Reader.new(shared: @shared)
|
||||
end
|
||||
|
||||
def import_failure_service
|
||||
@import_failure_service ||= ImportFailureService.new(@project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -73,13 +73,17 @@ module Gitlab
|
|||
|
||||
relation_object.assign_attributes(importable_class_sym => @importable)
|
||||
|
||||
import_failure_service.with_retry(relation_key, relation_index) do
|
||||
import_failure_service.with_retry(action: 'relation_object.save!', relation_key: relation_key, relation_index: relation_index) do
|
||||
relation_object.save!
|
||||
end
|
||||
|
||||
save_id_mapping(relation_key, data_hash, relation_object)
|
||||
rescue => e
|
||||
import_failure_service.log_import_failure(relation_key, relation_index, e)
|
||||
import_failure_service.log_import_failure(
|
||||
source: 'process_relation_item!',
|
||||
relation_key: relation_key,
|
||||
relation_index: relation_index,
|
||||
exception: e)
|
||||
end
|
||||
|
||||
def import_failure_service
|
||||
|
|
|
@ -12,12 +12,12 @@ module Gitlab
|
|||
ERROR_MESSAGE = 'You cannot perform write operations on a read-only instance'
|
||||
|
||||
WHITELISTED_GIT_ROUTES = {
|
||||
'projects/git_http' => %w{git_upload_pack git_receive_pack}
|
||||
'repositories/git_http' => %w{git_upload_pack git_receive_pack}
|
||||
}.freeze
|
||||
|
||||
WHITELISTED_GIT_LFS_ROUTES = {
|
||||
'projects/lfs_api' => %w{batch},
|
||||
'projects/lfs_locks_api' => %w{verify create unlock}
|
||||
'repositories/lfs_api' => %w{batch},
|
||||
'repositories/lfs_locks_api' => %w{verify create unlock}
|
||||
}.freeze
|
||||
|
||||
WHITELISTED_GIT_REVISION_ROUTES = {
|
||||
|
|
|
@ -6,6 +6,7 @@ module Gitlab
|
|||
module SidekiqConfig
|
||||
FOSS_QUEUE_CONFIG_PATH = 'app/workers/all_queues.yml'
|
||||
EE_QUEUE_CONFIG_PATH = 'ee/app/workers/all_queues.yml'
|
||||
SIDEKIQ_QUEUES_PATH = 'config/sidekiq_queues.yml'
|
||||
|
||||
QUEUE_CONFIG_PATHS = [
|
||||
FOSS_QUEUE_CONFIG_PATH,
|
||||
|
@ -13,11 +14,19 @@ module Gitlab
|
|||
].compact.freeze
|
||||
|
||||
# For queues that don't have explicit workers - default and mailers
|
||||
DummyWorker = Struct.new(:queue)
|
||||
DummyWorker = Struct.new(:queue, :weight) do
|
||||
def queue_namespace
|
||||
nil
|
||||
end
|
||||
|
||||
def get_weight
|
||||
weight
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULT_WORKERS = [
|
||||
Gitlab::SidekiqConfig::Worker.new(DummyWorker.new('default'), ee: false),
|
||||
Gitlab::SidekiqConfig::Worker.new(DummyWorker.new('mailers'), ee: false)
|
||||
Gitlab::SidekiqConfig::Worker.new(DummyWorker.new('default', 1), ee: false),
|
||||
Gitlab::SidekiqConfig::Worker.new(DummyWorker.new('mailers', 2), ee: false)
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
|
@ -30,7 +39,7 @@ module Gitlab
|
|||
|
||||
def config_queues
|
||||
@config_queues ||= begin
|
||||
config = YAML.load_file(Rails.root.join('config/sidekiq_queues.yml'))
|
||||
config = YAML.load_file(Rails.root.join(SIDEKIQ_QUEUES_PATH))
|
||||
config[:queues].map(&:first)
|
||||
end
|
||||
end
|
||||
|
@ -65,6 +74,28 @@ module Gitlab
|
|||
Gitlab.ee? && ee_workers != YAML.safe_load(File.read(EE_QUEUE_CONFIG_PATH))
|
||||
end
|
||||
|
||||
def queues_for_sidekiq_queues_yml
|
||||
namespaces_with_equal_weights =
|
||||
workers
|
||||
.group_by(&:queue_namespace)
|
||||
.map(&:last)
|
||||
.select { |workers| workers.map(&:get_weight).uniq.count == 1 }
|
||||
.map(&:first)
|
||||
|
||||
namespaces = namespaces_with_equal_weights.map(&:queue_namespace).to_set
|
||||
remaining_queues = workers.reject { |worker| namespaces.include?(worker.queue_namespace) }
|
||||
|
||||
(namespaces_with_equal_weights.map(&:namespace_and_weight) +
|
||||
remaining_queues.map(&:queue_and_weight)).sort
|
||||
end
|
||||
|
||||
def sidekiq_queues_yml_outdated?
|
||||
# YAML.load is OK here as we control the file contents
|
||||
config_queues = YAML.load(File.read(SIDEKIQ_QUEUES_PATH))[:queues] # rubocop:disable Security/YAMLLoad
|
||||
|
||||
queues_for_sidekiq_queues_yml != config_queues
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_workers(root, ee:)
|
||||
|
|
|
@ -7,8 +7,9 @@ module Gitlab
|
|||
|
||||
attr_reader :klass
|
||||
delegate :feature_category_not_owned?, :get_feature_category,
|
||||
:get_worker_resource_boundary, :latency_sensitive_worker?,
|
||||
:queue, :worker_has_external_dependencies?,
|
||||
:get_weight, :get_worker_resource_boundary,
|
||||
:latency_sensitive_worker?, :queue, :queue_namespace,
|
||||
:worker_has_external_dependencies?,
|
||||
to: :klass
|
||||
|
||||
def initialize(klass, ee:)
|
||||
|
@ -35,7 +36,7 @@ module Gitlab
|
|||
|
||||
# Put namespaced queues first
|
||||
def to_sort
|
||||
[queue.include?(':') ? 0 : 1, queue]
|
||||
[queue_namespace ? 0 : 1, queue]
|
||||
end
|
||||
|
||||
# YAML representation
|
||||
|
@ -46,6 +47,14 @@ module Gitlab
|
|||
def to_yaml
|
||||
queue
|
||||
end
|
||||
|
||||
def namespace_and_weight
|
||||
[queue_namespace, get_weight]
|
||||
end
|
||||
|
||||
def queue_and_weight
|
||||
[queue, get_weight]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,13 @@ return if Rails.env.production?
|
|||
|
||||
namespace :gitlab do
|
||||
namespace :sidekiq do
|
||||
def write_yaml(path, banner, object)
|
||||
File.write(path, banner + YAML.dump(object))
|
||||
end
|
||||
|
||||
namespace :all_queues_yml do
|
||||
def write_yaml(path, object)
|
||||
desc 'GitLab | Sidekiq | Generate all_queues.yml based on worker definitions'
|
||||
task generate: :environment do
|
||||
banner = <<~BANNER
|
||||
# This file is generated automatically by
|
||||
# bin/rake gitlab:sidekiq:all_queues_yml:generate
|
||||
|
@ -13,17 +18,12 @@ namespace :gitlab do
|
|||
# Do not edit it manually!
|
||||
BANNER
|
||||
|
||||
File.write(path, banner + YAML.dump(object))
|
||||
end
|
||||
|
||||
desc 'GitLab | Sidekiq | Generate all_queues.yml based on worker definitions'
|
||||
task generate: :environment do
|
||||
foss_workers, ee_workers = Gitlab::SidekiqConfig.workers_for_all_queues_yml
|
||||
|
||||
write_yaml(Gitlab::SidekiqConfig::FOSS_QUEUE_CONFIG_PATH, foss_workers)
|
||||
write_yaml(Gitlab::SidekiqConfig::FOSS_QUEUE_CONFIG_PATH, banner, foss_workers)
|
||||
|
||||
if Gitlab.ee?
|
||||
write_yaml(Gitlab::SidekiqConfig::EE_QUEUE_CONFIG_PATH, ee_workers)
|
||||
write_yaml(Gitlab::SidekiqConfig::EE_QUEUE_CONFIG_PATH, banner, ee_workers)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -44,5 +44,57 @@ namespace :gitlab do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace :sidekiq_queues_yml do
|
||||
desc 'GitLab | Sidekiq | Generate sidekiq_queues.yml based on worker definitions'
|
||||
task generate: :environment do
|
||||
banner = <<~BANNER
|
||||
# This file is generated automatically by
|
||||
# bin/rake gitlab:sidekiq:sidekiq_queues_yml:generate
|
||||
#
|
||||
# Do not edit it manually!
|
||||
#
|
||||
# This configuration file should be exclusively used to set queue settings for
|
||||
# Sidekiq. Any other setting should be specified using the Sidekiq CLI or the
|
||||
# Sidekiq Ruby API (see config/initializers/sidekiq.rb).
|
||||
#
|
||||
# All the queues to process and their weights. Every queue _must_ have a weight
|
||||
# defined.
|
||||
#
|
||||
# The available weights are as follows
|
||||
#
|
||||
# 1: low priority
|
||||
# 2: medium priority
|
||||
# 3: high priority
|
||||
# 5: _super_ high priority, this should only be used for _very_ important queues
|
||||
#
|
||||
# As per http://stackoverflow.com/a/21241357/290102 the formula for calculating
|
||||
# the likelihood of a job being popped off a queue (given all queues have work
|
||||
# to perform) is:
|
||||
#
|
||||
# chance = (queue weight / total weight of all queues) * 100
|
||||
BANNER
|
||||
|
||||
queues_and_weights = Gitlab::SidekiqConfig.queues_for_sidekiq_queues_yml
|
||||
|
||||
write_yaml(Gitlab::SidekiqConfig::SIDEKIQ_QUEUES_PATH, banner, queues: queues_and_weights)
|
||||
end
|
||||
|
||||
desc 'GitLab | Sidekiq | Validate that sidekiq_queues.yml matches worker definitions'
|
||||
task check: :environment do
|
||||
if Gitlab::SidekiqConfig.sidekiq_queues_yml_outdated?
|
||||
raise <<~MSG
|
||||
Changes in worker queues found, please update the metadata by running:
|
||||
|
||||
bin/rake gitlab:sidekiq:sidekiq_queues_yml:generate
|
||||
|
||||
Then commit and push the changes from:
|
||||
|
||||
- #{Gitlab::SidekiqConfig::SIDEKIQ_QUEUES_PATH}
|
||||
|
||||
MSG
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,11 +38,13 @@ unless Rails.env.production?
|
|||
]
|
||||
|
||||
if Gitlab.ee?
|
||||
# This task will fail on CE installations (e.g. gitlab-org/gitlab-foss)
|
||||
# since it will detect strings in the locale files that do not exist in
|
||||
# the source files. To work around this we will only enable this task on
|
||||
# EE installations.
|
||||
# These tasks will fail on FOSS installations
|
||||
# (e.g. gitlab-org/gitlab-foss) since they test against a single
|
||||
# file that is generated by an EE installation, which can
|
||||
# contain values that a FOSS installation won't find. To work
|
||||
# around this we will only enable this task on EE installations.
|
||||
tasks << 'gettext:updated_check'
|
||||
tasks << 'gitlab:sidekiq:sidekiq_queues_yml:check'
|
||||
end
|
||||
|
||||
tasks.each do |task|
|
||||
|
|
|
@ -10419,6 +10419,9 @@ msgstr ""
|
|||
msgid "Is using license seat:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Is using seat"
|
||||
msgstr ""
|
||||
|
||||
msgid "IssuableStatus|Closed"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ module QA::Page
|
|||
element :pipeline_path
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/jobs/components/sidebar.vue' do
|
||||
element :retry_button
|
||||
end
|
||||
|
||||
def successful?(timeout: 60)
|
||||
raise "Timed out waiting for the build trace to load" unless loaded?
|
||||
raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout)
|
||||
|
@ -33,6 +37,10 @@ module QA::Page
|
|||
result
|
||||
end
|
||||
|
||||
def retry!
|
||||
click_element :retry_button
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def loaded?(wait: 60)
|
||||
|
|
|
@ -52,6 +52,14 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def remove_variable(location: :first)
|
||||
within('.ci-variable-row-body', match: location) do
|
||||
find('button.ci-variable-row-remove-button').click
|
||||
end
|
||||
|
||||
save_variables
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toggle_masked(masked_node, masked)
|
||||
|
|
|
@ -46,12 +46,7 @@ module QA
|
|||
|
||||
deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}"
|
||||
|
||||
Resource::CiVariable.fabricate_via_browser_ui! do |resource|
|
||||
resource.project = @project
|
||||
resource.key = deploy_key_name
|
||||
resource.value = key.private_key
|
||||
resource.masked = false
|
||||
end
|
||||
make_ci_variable(deploy_key_name, key)
|
||||
|
||||
gitlab_ci = <<~YAML
|
||||
cat-config:
|
||||
|
@ -90,6 +85,17 @@ module QA
|
|||
expect(job.output).to include(sha1sum)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_ci_variable(key_name, key)
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = @project
|
||||
resource.key = key_name
|
||||
resource.value = key.private_key
|
||||
resource.masked = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'spec_helper'
|
|||
describe LfsRequest do
|
||||
include ProjectForksHelper
|
||||
|
||||
controller(Projects::GitHttpClientController) do
|
||||
controller(Repositories::GitHttpClientController) do
|
||||
# `described_class` is not available in this context
|
||||
include LfsRequest
|
||||
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Projects::GitHttpController do
|
||||
include GitHttpHelpers
|
||||
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let(:project_params) do
|
||||
{
|
||||
namespace_id: project.namespace.to_param,
|
||||
project_id: project.path + '.git'
|
||||
}
|
||||
end
|
||||
let(:params) { project_params }
|
||||
|
||||
describe 'HEAD #info_refs' do
|
||||
it 'returns 403' do
|
||||
head :info_refs, params: { namespace_id: project.namespace.to_param, project_id: project.path + '.git' }
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #info_refs' do
|
||||
let(:params) { project_params.merge(service: 'git-upload-pack') }
|
||||
|
||||
it 'returns 401 for unauthenticated requests to public repositories when http protocol is disabled' do
|
||||
stub_application_setting(enabled_git_access_protocol: 'ssh')
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
end
|
||||
|
||||
context 'with authorized user' do
|
||||
let(:user) { project.owner }
|
||||
|
||||
before do
|
||||
request.headers.merge! auth_env(user.username, user.password, nil)
|
||||
end
|
||||
|
||||
it 'returns 200' do
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it 'updates the user activity' do
|
||||
expect_next_instance_of(Users::ActivityService) do |activity_service|
|
||||
expect(activity_service).to receive(:execute)
|
||||
end
|
||||
|
||||
get :info_refs, params: params
|
||||
end
|
||||
end
|
||||
|
||||
context 'with exceptions' do
|
||||
before do
|
||||
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns 503 with GRPC Unavailable' do
|
||||
allow(controller).to receive(:access_check).and_raise(GRPC::Unavailable)
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(503)
|
||||
end
|
||||
|
||||
it 'returns 503 with timeout error' do
|
||||
allow(controller).to receive(:access_check).and_raise(Gitlab::GitAccess::TimeoutError)
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(503)
|
||||
expect(response.body).to eq 'Gitlab::GitAccess::TimeoutError'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #git_upload_pack' do
|
||||
before do
|
||||
allow(controller).to receive(:authenticate_user).and_return(true)
|
||||
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
|
||||
allow(controller).to receive(:access_check).and_return(nil)
|
||||
end
|
||||
|
||||
after do
|
||||
post :git_upload_pack, params: params
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
end
|
||||
|
||||
it 'does not update project statistics' do
|
||||
expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates project statistics' do
|
||||
expect(ProjectDailyStatisticsWorker).to receive(:perform_async)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,146 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Repositories::GitHttpController do
|
||||
include GitHttpHelpers
|
||||
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
|
||||
let(:namespace_id) { project.namespace.to_param }
|
||||
let(:repository_id) { project.path + '.git' }
|
||||
let(:project_params) do
|
||||
{
|
||||
namespace_id: namespace_id,
|
||||
repository_id: repository_id
|
||||
}
|
||||
end
|
||||
let(:params) { project_params }
|
||||
|
||||
describe 'HEAD #info_refs' do
|
||||
it 'returns 403' do
|
||||
head :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'info_refs behavior' do
|
||||
describe 'GET #info_refs' do
|
||||
let(:params) { project_params.merge(service: 'git-upload-pack') }
|
||||
|
||||
it 'returns 401 for unauthenticated requests to public repositories when http protocol is disabled' do
|
||||
stub_application_setting(enabled_git_access_protocol: 'ssh')
|
||||
allow(controller).to receive(:basic_auth_provided?).and_call_original
|
||||
|
||||
expect(controller).to receive(:http_download_allowed?).and_call_original
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
end
|
||||
|
||||
context 'with authorized user' do
|
||||
let(:user) { project.owner }
|
||||
|
||||
before do
|
||||
request.headers.merge! auth_env(user.username, user.password, nil)
|
||||
end
|
||||
|
||||
it 'returns 200' do
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it 'updates the user activity' do
|
||||
expect_next_instance_of(Users::ActivityService) do |activity_service|
|
||||
expect(activity_service).to receive(:execute)
|
||||
end
|
||||
|
||||
get :info_refs, params: params
|
||||
end
|
||||
end
|
||||
|
||||
context 'with exceptions' do
|
||||
before do
|
||||
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns 503 with GRPC Unavailable' do
|
||||
allow(controller).to receive(:access_check).and_raise(GRPC::Unavailable)
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(503)
|
||||
end
|
||||
|
||||
it 'returns 503 with timeout error' do
|
||||
allow(controller).to receive(:access_check).and_raise(Gitlab::GitAccess::TimeoutError)
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response.status).to eq(503)
|
||||
expect(response.body).to eq 'Gitlab::GitAccess::TimeoutError'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'git_upload_pack behavior' do |expected|
|
||||
describe 'POST #git_upload_pack' do
|
||||
before do
|
||||
allow(controller).to receive(:authenticate_user).and_return(true)
|
||||
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
|
||||
allow(controller).to receive(:access_check).and_return(nil)
|
||||
end
|
||||
|
||||
after do
|
||||
post :git_upload_pack, params: params
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
end
|
||||
|
||||
it 'does not update project statistics' do
|
||||
expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
if expected
|
||||
it 'updates project statistics' do
|
||||
expect(ProjectDailyStatisticsWorker).to receive(:perform_async)
|
||||
end
|
||||
else
|
||||
it 'does not update project statistics' do
|
||||
expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'access checker class' do
|
||||
let(:params) { project_params.merge(service: 'git-upload-pack') }
|
||||
|
||||
it 'calls the right access class checker with the right object' do
|
||||
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
|
||||
|
||||
access_double = double
|
||||
expect(expected_class).to receive(:new).with(anything, expected_object, 'http', anything).and_return(access_double)
|
||||
allow(access_double).to receive(:check).and_return(false)
|
||||
|
||||
get :info_refs, params: params
|
||||
end
|
||||
end
|
||||
|
||||
context 'when repository container is a project' do
|
||||
it_behaves_like 'info_refs behavior'
|
||||
it_behaves_like 'git_upload_pack behavior', true
|
||||
it_behaves_like 'access checker class' do
|
||||
let(:expected_class) { Gitlab::GitAccess }
|
||||
let(:expected_object) { project }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20,6 +20,7 @@ describe GitlabSchema.types['SentryDetailedError'] do
|
|||
message
|
||||
culprit
|
||||
externalUrl
|
||||
externalBaseUrl
|
||||
sentryProjectId
|
||||
sentryProjectName
|
||||
sentryProjectSlug
|
||||
|
@ -30,8 +31,10 @@ describe GitlabSchema.types['SentryDetailedError'] do
|
|||
lastReleaseLastCommit
|
||||
firstReleaseShortVersion
|
||||
lastReleaseShortVersion
|
||||
gitlabIssuePath
|
||||
gitlabCommit
|
||||
gitlabCommitPath
|
||||
tags
|
||||
]
|
||||
|
||||
is_expected.to have_graphql_fields(*expected_fields)
|
||||
|
|
|
@ -66,6 +66,53 @@ describe Gitaly::Server do
|
|||
end
|
||||
end
|
||||
|
||||
context "when examining disk statistics for a given server" do
|
||||
let(:disk_available) { 42 }
|
||||
let(:disk_used) { 42 }
|
||||
let(:storage_status) { double('storage_status') }
|
||||
|
||||
before do
|
||||
allow(storage_status).to receive(:storage_name).and_return('default')
|
||||
allow(storage_status).to receive(:available).and_return(disk_available)
|
||||
allow(storage_status).to receive(:used).and_return(disk_used)
|
||||
response = double("response")
|
||||
allow(response).to receive(:storage_statuses).and_return([storage_status])
|
||||
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
|
||||
allow(instance).to receive(:disk_statistics).and_return(response)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#disk_available' do
|
||||
subject { server.disk_available }
|
||||
|
||||
it { is_expected.to be_present }
|
||||
|
||||
it "returns disk available for the storage of the instantiated server" do
|
||||
is_expected.to eq(disk_available)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#disk_used' do
|
||||
subject { server.disk_used }
|
||||
|
||||
it { is_expected.to be_present }
|
||||
|
||||
it "returns disk used for the storage of the instantiated server" do
|
||||
is_expected.to eq(disk_used)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#disk_stats' do
|
||||
subject { server.disk_stats }
|
||||
|
||||
it { is_expected.to be_present }
|
||||
|
||||
it "returns the storage of the instantiated server" do
|
||||
is_expected.to eq(storage_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expected_version?' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ describe Gitlab::GitalyClient do
|
|||
end
|
||||
|
||||
describe '.filesystem_id' do
|
||||
it 'returns an empty string when the storage is not found in the response' do
|
||||
it 'returns an empty string when the relevant storage status is not found in the response' do
|
||||
response = double("response")
|
||||
allow(response).to receive(:storage_statuses).and_return([])
|
||||
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
|
||||
|
@ -63,6 +63,63 @@ describe Gitlab::GitalyClient do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the relevant storage status is not found' do
|
||||
before do
|
||||
response = double('response')
|
||||
allow(response).to receive(:storage_statuses).and_return([])
|
||||
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
|
||||
allow(instance).to receive(:disk_statistics).and_return(response)
|
||||
expect(instance).to receive(:storage_disk_statistics)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.filesystem_disk_available' do
|
||||
it 'returns nil when the relevant storage status is not found in the response' do
|
||||
expect(described_class.filesystem_disk_available('default')).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.filesystem_disk_used' do
|
||||
it 'returns nil when the relevant storage status is not found in the response' do
|
||||
expect(described_class.filesystem_disk_used('default')).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the relevant storage status is found' do
|
||||
let(:disk_available) { 42 }
|
||||
let(:disk_used) { 42 }
|
||||
let(:storage_status) { double('storage_status') }
|
||||
|
||||
before do
|
||||
allow(storage_status).to receive(:storage_name).and_return('default')
|
||||
allow(storage_status).to receive(:used).and_return(disk_used)
|
||||
allow(storage_status).to receive(:available).and_return(disk_available)
|
||||
response = double('response')
|
||||
allow(response).to receive(:storage_statuses).and_return([storage_status])
|
||||
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
|
||||
allow(instance).to receive(:disk_statistics).and_return(response)
|
||||
end
|
||||
expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
|
||||
expect(instance).to receive(:storage_disk_statistics).and_return(storage_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.filesystem_disk_available' do
|
||||
it 'returns disk available when the relevant storage status is found in the response' do
|
||||
expect(storage_status).to receive(:available)
|
||||
expect(described_class.filesystem_disk_available('default')).to eq(disk_available)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.filesystem_disk_used' do
|
||||
it 'returns disk used when the relevant storage status is found in the response' do
|
||||
expect(storage_status).to receive(:used)
|
||||
expect(described_class.filesystem_disk_used('default')).to eq(disk_used)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.stub_class' do
|
||||
it 'returns the gRPC health check stub' do
|
||||
expect(described_class.stub_class(:health_check)).to eq(::Grpc::Health::V1::Health::Stub)
|
||||
|
|
|
@ -6,6 +6,7 @@ describe Gitlab::ImportExport::ImportFailureService do
|
|||
let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') }
|
||||
let(:label) { create(:label) }
|
||||
let(:subject) { described_class.new(importable) }
|
||||
let(:action) { "save_relation" }
|
||||
let(:relation_key) { "labels" }
|
||||
let(:relation_index) { 0 }
|
||||
|
||||
|
@ -15,7 +16,12 @@ describe Gitlab::ImportExport::ImportFailureService do
|
|||
let(:correlation_id) { 'my-correlation-id' }
|
||||
let(:retry_count) { 2 }
|
||||
let(:log_import_failure) do
|
||||
subject.log_import_failure(relation_key, relation_index, exception, retry_count)
|
||||
subject.log_import_failure(
|
||||
source: action,
|
||||
relation_key: relation_key,
|
||||
relation_index: relation_index,
|
||||
exception: exception,
|
||||
retry_count: retry_count)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -44,7 +50,7 @@ describe Gitlab::ImportExport::ImportFailureService do
|
|||
|
||||
describe '#with_retry' do
|
||||
let(:perform_retry) do
|
||||
subject.with_retry(relation_key, relation_index) do
|
||||
subject.with_retry(action: action, relation_key: relation_key, relation_index: relation_index) do
|
||||
label.save!
|
||||
end
|
||||
end
|
||||
|
@ -60,7 +66,12 @@ describe Gitlab::ImportExport::ImportFailureService do
|
|||
end
|
||||
|
||||
it 'retries and logs import failure once with correct params' do
|
||||
expect(subject).to receive(:log_import_failure).with(relation_key, relation_index, instance_of(exception), 1).once
|
||||
expect(subject).to receive(:log_import_failure).with(
|
||||
source: action,
|
||||
relation_key: relation_key,
|
||||
relation_index: relation_index,
|
||||
exception: instance_of(exception),
|
||||
retry_count: 1).once
|
||||
|
||||
perform_retry
|
||||
end
|
||||
|
@ -85,7 +96,11 @@ describe Gitlab::ImportExport::ImportFailureService do
|
|||
maximum_retry_count.times do |index|
|
||||
retry_count = index + 1
|
||||
|
||||
expect(subject).to receive(:log_import_failure).with(relation_key, relation_index, instance_of(exception), retry_count)
|
||||
expect(subject).to receive(:log_import_failure).with(
|
||||
source: action, relation_key: relation_key,
|
||||
relation_index: relation_index,
|
||||
exception: instance_of(exception),
|
||||
retry_count: retry_count)
|
||||
end
|
||||
|
||||
expect { perform_retry }.to raise_exception(exception)
|
||||
|
|
|
@ -498,6 +498,58 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when post import action throw non-retriable exception' do
|
||||
let(:exception) { StandardError.new('post_import_error') }
|
||||
|
||||
before do
|
||||
setup_import_export_config('light')
|
||||
expect(project)
|
||||
.to receive(:merge_requests)
|
||||
.and_raise(exception)
|
||||
end
|
||||
|
||||
it 'report post import error' do
|
||||
expect(restored_project_json).to eq(false)
|
||||
expect(shared.errors).to include('post_import_error')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when post import action throw retriable exception one time' do
|
||||
let(:exception) { GRPC::DeadlineExceeded.new }
|
||||
|
||||
before do
|
||||
setup_import_export_config('light')
|
||||
expect(project)
|
||||
.to receive(:merge_requests)
|
||||
.and_raise(exception)
|
||||
expect(project)
|
||||
.to receive(:merge_requests)
|
||||
.and_call_original
|
||||
expect(restored_project_json).to eq(true)
|
||||
end
|
||||
|
||||
it_behaves_like 'restores project successfully',
|
||||
issues: 1,
|
||||
labels: 2,
|
||||
label_with_priorities: 'A project label',
|
||||
milestones: 1,
|
||||
first_issue_labels: 1,
|
||||
services: 1,
|
||||
import_failures: 1
|
||||
|
||||
it 'records the failures in the database' do
|
||||
import_failure = ImportFailure.last
|
||||
|
||||
expect(import_failure.project_id).to eq(project.id)
|
||||
expect(import_failure.relation_key).to be_nil
|
||||
expect(import_failure.relation_index).to be_nil
|
||||
expect(import_failure.exception_class).to eq('GRPC::DeadlineExceeded')
|
||||
expect(import_failure.exception_message).to be_present
|
||||
expect(import_failure.correlation_id_value).not_to be_empty
|
||||
expect(import_failure.created_at).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project has overridden params in import data' do
|
||||
before do
|
||||
setup_import_export_config('light')
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
require 'fast_spec_helper'
|
||||
|
||||
describe Gitlab::SidekiqConfig::Worker do
|
||||
def worker_with_queue(queue)
|
||||
described_class.new(double(queue: queue), ee: false)
|
||||
def create_worker(queue:, weight: 0)
|
||||
namespace = queue.include?(':') && queue.split(':').first
|
||||
inner_worker = double(queue: queue, queue_namespace: namespace, get_weight: weight)
|
||||
|
||||
described_class.new(inner_worker, ee: false)
|
||||
end
|
||||
|
||||
describe '#ee?' do
|
||||
|
@ -34,9 +37,9 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
|
||||
describe 'delegations' do
|
||||
[
|
||||
:feature_category_not_owned?, :get_feature_category,
|
||||
:feature_category_not_owned?, :get_feature_category, :get_weight,
|
||||
:get_worker_resource_boundary, :latency_sensitive_worker?, :queue,
|
||||
:worker_has_external_dependencies?
|
||||
:queue_namespace, :worker_has_external_dependencies?
|
||||
].each do |meth|
|
||||
it "delegates #{meth} to the worker class" do
|
||||
worker = double
|
||||
|
@ -50,8 +53,8 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
|
||||
describe 'sorting' do
|
||||
it 'sorts queues with a namespace before those without a namespace' do
|
||||
namespaced_worker = worker_with_queue('namespace:queue')
|
||||
plain_worker = worker_with_queue('a_queue')
|
||||
namespaced_worker = create_worker(queue: 'namespace:queue')
|
||||
plain_worker = create_worker(queue: 'a_queue')
|
||||
|
||||
expect([plain_worker, namespaced_worker].sort)
|
||||
.to eq([namespaced_worker, plain_worker])
|
||||
|
@ -59,12 +62,12 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
|
||||
it 'sorts alphabetically by queue' do
|
||||
workers = [
|
||||
worker_with_queue('namespace:a'),
|
||||
worker_with_queue('namespace:b'),
|
||||
worker_with_queue('other_namespace:a'),
|
||||
worker_with_queue('other_namespace:b'),
|
||||
worker_with_queue('a'),
|
||||
worker_with_queue('b')
|
||||
create_worker(queue: 'namespace:a'),
|
||||
create_worker(queue: 'namespace:b'),
|
||||
create_worker(queue: 'other_namespace:a'),
|
||||
create_worker(queue: 'other_namespace:b'),
|
||||
create_worker(queue: 'a'),
|
||||
create_worker(queue: 'b')
|
||||
]
|
||||
|
||||
expect(workers.shuffle.sort).to eq(workers)
|
||||
|
@ -73,12 +76,26 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
|
||||
describe 'YAML encoding' do
|
||||
it 'encodes the worker in YAML as a string of the queue' do
|
||||
worker_a = worker_with_queue('a')
|
||||
worker_b = worker_with_queue('b')
|
||||
worker_a = create_worker(queue: 'a')
|
||||
worker_b = create_worker(queue: 'b')
|
||||
|
||||
expect(YAML.dump(worker_a)).to eq(YAML.dump('a'))
|
||||
expect(YAML.dump([worker_a, worker_b]))
|
||||
.to eq(YAML.dump(%w[a b]))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#namespace_and_weight' do
|
||||
it 'returns a namespace, weight pair for the worker' do
|
||||
expect(create_worker(queue: 'namespace:a', weight: 2).namespace_and_weight)
|
||||
.to eq(['namespace', 2])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#queue_and_weight' do
|
||||
it 'returns a queue, weight pair for the worker' do
|
||||
expect(create_worker(queue: 'namespace:a', weight: 2).queue_and_weight)
|
||||
.to eq(['namespace:a', 2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -80,4 +80,64 @@ describe Gitlab::SidekiqConfig do
|
|||
expect(described_class.all_queues_yml_outdated?).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.queues_for_sidekiq_queues_yml' do
|
||||
before do
|
||||
workers = [
|
||||
Namespaces::RootStatisticsWorker,
|
||||
Namespaces::ScheduleAggregationWorker,
|
||||
MergeWorker,
|
||||
ProcessCommitWorker
|
||||
].map { |worker| described_class::Worker.new(worker, ee: false) }
|
||||
|
||||
allow(described_class).to receive(:workers).and_return(workers)
|
||||
end
|
||||
|
||||
it 'returns queues and weights, aggregating namespaces with the same weight' do
|
||||
expected_queues = [
|
||||
['merge', 5],
|
||||
['process_commit', 3],
|
||||
['update_namespace_statistics', 1]
|
||||
]
|
||||
|
||||
expect(described_class.queues_for_sidekiq_queues_yml).to eq(expected_queues)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.sidekiq_queues_yml_outdated?' do
|
||||
before do
|
||||
workers = [
|
||||
Namespaces::RootStatisticsWorker,
|
||||
Namespaces::ScheduleAggregationWorker,
|
||||
MergeWorker,
|
||||
ProcessCommitWorker
|
||||
].map { |worker| described_class::Worker.new(worker, ee: false) }
|
||||
|
||||
allow(described_class).to receive(:workers).and_return(workers)
|
||||
end
|
||||
|
||||
let(:expected_queues) do
|
||||
[
|
||||
['merge', 5],
|
||||
['process_commit', 3],
|
||||
['update_namespace_statistics', 1]
|
||||
]
|
||||
end
|
||||
|
||||
it 'returns true if the YAML file does not match the application code' do
|
||||
allow(File).to receive(:read)
|
||||
.with(described_class::SIDEKIQ_QUEUES_PATH)
|
||||
.and_return(YAML.dump(queues: expected_queues.reverse))
|
||||
|
||||
expect(described_class.sidekiq_queues_yml_outdated?).to be(true)
|
||||
end
|
||||
|
||||
it 'returns false if the YAML file matches the application code' do
|
||||
allow(File).to receive(:read)
|
||||
.with(described_class::SIDEKIQ_QUEUES_PATH)
|
||||
.and_return(YAML.dump(queues: expected_queues))
|
||||
|
||||
expect(described_class.sidekiq_queues_yml_outdated?).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2391,6 +2391,8 @@ describe Ci::Build do
|
|||
{ key: 'GITLAB_CI', value: 'true', public: true, masked: false },
|
||||
{ key: 'CI_SERVER_URL', value: Gitlab.config.gitlab.url, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_PORT', value: Gitlab.config.gitlab.port.to_s, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_PROTOCOL', value: Gitlab.config.gitlab.protocol, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true, masked: false },
|
||||
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true, masked: false },
|
||||
|
|
|
@ -8,22 +8,10 @@ describe API::BroadcastMessages do
|
|||
set(:message) { create(:broadcast_message) }
|
||||
|
||||
describe 'GET /broadcast_messages' do
|
||||
it 'returns a 401 for anonymous users' do
|
||||
get api('/broadcast_messages')
|
||||
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
end
|
||||
|
||||
it 'returns a 403 for users' do
|
||||
get api('/broadcast_messages', user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
end
|
||||
|
||||
it 'returns an Array of BroadcastMessages for admins' do
|
||||
it 'returns an Array of BroadcastMessages' do
|
||||
create(:broadcast_message)
|
||||
|
||||
get api('/broadcast_messages', admin)
|
||||
get api('/broadcast_messages')
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to include_pagination_headers
|
||||
|
@ -34,21 +22,9 @@ describe API::BroadcastMessages do
|
|||
end
|
||||
|
||||
describe 'GET /broadcast_messages/:id' do
|
||||
it 'returns a 401 for anonymous users' do
|
||||
it 'returns the specified message' do
|
||||
get api("/broadcast_messages/#{message.id}")
|
||||
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
end
|
||||
|
||||
it 'returns a 403 for users' do
|
||||
get api("/broadcast_messages/#{message.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
end
|
||||
|
||||
it 'returns the specified message for admins' do
|
||||
get api("/broadcast_messages/#{message.id}", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(json_response['id']).to eq message.id
|
||||
expect(json_response.keys)
|
||||
|
|
|
@ -57,6 +57,10 @@ describe 'getting a detailed sentry error' do
|
|||
expect(error_data['firstSeen']).to eql sentry_detailed_error.first_seen
|
||||
expect(error_data['lastSeen']).to eql sentry_detailed_error.last_seen
|
||||
expect(error_data['gitlabCommit']).to be nil
|
||||
expect(error_data['externalBaseUrl']).to eq sentry_detailed_error.external_base_url
|
||||
expect(error_data['gitlabIssuePath']).to eq sentry_detailed_error.gitlab_issue
|
||||
expect(error_data['tags']['logger']).to eq sentry_detailed_error.tags[:logger]
|
||||
expect(error_data['tags']['level']).to eq sentry_detailed_error.tags[:level]
|
||||
end
|
||||
|
||||
it 'is expected to return the frequency correctly' do
|
||||
|
|
|
@ -326,7 +326,7 @@ describe API::Internal::Base do
|
|||
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
|
||||
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
|
||||
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
|
||||
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true')
|
||||
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true')
|
||||
expect(user.reload.last_activity_on).to eql(Date.today)
|
||||
end
|
||||
end
|
||||
|
@ -346,7 +346,7 @@ describe API::Internal::Base do
|
|||
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
|
||||
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
|
||||
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
|
||||
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true')
|
||||
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true')
|
||||
expect(user.reload.last_activity_on).to be_nil
|
||||
end
|
||||
end
|
||||
|
@ -594,7 +594,7 @@ describe API::Internal::Base do
|
|||
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
|
||||
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
|
||||
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
|
||||
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true')
|
||||
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true', 'gitaly-feature-commit-without-batch-check' => 'true')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue