Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-01-24 18:09:00 +00:00
parent 3e36f70be4
commit 411cc77938
102 changed files with 1842 additions and 958 deletions

View File

@ -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)

View File

@ -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',

View File

@ -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
>

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module Repositories
class ApplicationController < ::ApplicationController
skip_before_action :authenticate_user!
end
end

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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",

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -5,6 +5,7 @@ class EmailReceiverWorker
feature_category :issue_tracking
latency_sensitive_worker!
weight 2
def perform(raw)
return unless Gitlab::IncomingEmail.enabled?

View File

@ -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!

View File

@ -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

View File

@ -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

View File

@ -4,6 +4,7 @@ class InvalidGpgSignatureUpdateWorker
include ApplicationWorker
feature_category :source_code_management
weight 2
# rubocop: disable CodeReuse/ActiveRecord
def perform(gpg_key_id)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,5 @@
---
title: Add WAF Anomaly Summary service
merge_request: 22736
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Add tags, external_base_url, gitlab_issue to Sentry Detailed Error graphql
merge_request: 23483
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Remove button group for edit and web ide in file header
merge_request: 23291
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Add CI variables to provide GitLab port and protocol
merge_request: 23296
author: Aidin Abedi
type: added

View File

@ -0,0 +1,5 @@
---
title: Allow users to read broadcast messages via API
merge_request: 23298
author: Rajendra Kadam
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add accidentally deleted project config for custom apply suggestions
merge_request: 23687
author: Fabio Huser
type: fixed

View File

@ -0,0 +1,5 @@
---
title: "Support retrieval of disk statistics from Gitaly"
merge_request: 22226
author: Nels Nelson
type: added

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)"

View File

@ -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:

View File

@ -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
"""

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = {

View File

@ -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:)

View File

@ -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

View File

@ -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

View File

@ -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|

View File

@ -10419,6 +10419,9 @@ msgstr ""
msgid "Is using license seat:"
msgstr ""
msgid "Is using seat"
msgstr ""
msgid "IssuableStatus|Closed"
msgstr ""

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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 },

View File

@ -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)

View File

@ -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

View File

@ -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