Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot
2023-08-15 06:09:36 +00:00
parent b291dca0e5
commit 024f77efd6
38 changed files with 499 additions and 205 deletions

View File

@ -2743,7 +2743,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/bulk_imports/common/extractors/json_extractor_spec.rb'
- 'spec/lib/bulk_imports/common/extractors/ndjson_extractor_spec.rb'
- 'spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb'
- 'spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb'
- 'spec/lib/bulk_imports/common/pipelines/badges_pipeline_spec.rb'
- 'spec/lib/bulk_imports/common/pipelines/entity_finisher_spec.rb'
- 'spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb'

View File

@ -1 +1 @@
v16.3.0-rc6
v16.3.0

View File

@ -24,7 +24,7 @@ module Groups
case user_or_deploy_token
when User
@authentication_result = Gitlab::Auth::Result.new(user_or_deploy_token, nil, :user, [])
sign_in(user_or_deploy_token)
sign_in(user_or_deploy_token) unless user_or_deploy_token.project_bot?
when DeployToken
@authentication_result = Gitlab::Auth::Result.new(user_or_deploy_token, nil, :deploy_token, [])
end

View File

@ -8,6 +8,7 @@ module Types
value 'text', description: "Text file."
value 'image', description: "An image."
value 'file', description: "Unknown file type."
end
end
end

View File

@ -23,7 +23,7 @@
= f.label :gitpod_url, s_('Gitpod|Gitpod URL'), class: 'label-bold'
= f.text_field :gitpod_url, class: 'form-control gl-form-input', placeholder: s_('Gitpod|https://gitpod.example.com')
.form-text.text-muted
- help_link = link_to('', help_page_path('integration/gitpod', anchor: 'enable-gitpod-in-your-user-settings', target: '_blank', rel: 'noopener noreferrer'))
= s_('Gitpod|The URL to your Gitpod instance configured to read your GitLab projects, such as https://gitpod.example.com.')
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('integration/gitpod', anchor: 'enable-gitpod-in-your-user-settings') }
= s_('Gitpod|To use the integration, each user must also enable Gitpod on their GitLab account. %{link_start}How do I enable it?%{link_end} ').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
= safe_format(s_('Gitpod|To use the integration, each user must also enable Gitpod on their GitLab account. %{help_link_start}How do I enable it?%{help_link_end}'), tag_pair(help_link, :help_link_start, :help_link_end))
= f.submit _('Save changes'), pajamas_button: true

View File

@ -6,8 +6,9 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('development/snowplow/index') }
= html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank', rel: 'noopener noreferrer').html_safe, link_start: link_start, link_end: '</a>'.html_safe }
- help_link = link_to('', help_page_path('development/snowplow/index'), target: '_blank', rel: 'noopener noreferrer')
- snowplow_link = link_to('', 'https://snowplow.io/', target: '_blank', rel: 'noopener noreferrer')
= safe_format(_('Configure %{snowplow_link_start}Snowplow%{snowplow_link_end} to track events. %{help_link_start}Learn more.%{help_link_end}'), tag_pair(snowplow_link, :snowplow_link_start, :snowplow_link_end), tag_pair(help_link, :help_link_start, :help_link_end))
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
= form_errors(@application_setting) if expanded

View File

@ -9,8 +9,8 @@
.settings-content
- if ci_variable_protected_by_default?
%p.settings-message.text-center.gl-mb-0
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable') }
= s_('Environment variables on this GitLab instance are configured to be %{link_start}protected%{link_end} by default.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
- help_link = link_to('', help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable', target: '_blank', rel: 'noopener noreferrer'))
= safe_format(s_('Environment variables on this GitLab instance are configured to be %{help_link_start}protected%{help_link_end} by default.'), tag_pair(help_link, :help_link_start, :help_link_end))
#js-instance-variables{ data: { endpoint: admin_ci_variables_path, maskable_regex: ci_variable_maskable_regex, protected_by_default: ci_variable_protected_by_default?.to_s} }
%section.settings.as-ci-cd.no-animate#js-ci-cd-settings{ class: ('expanded' if expanded_by_default?) }

View File

@ -23,9 +23,7 @@
title: _('Service Ping payload not found in the application cache')) do |c|
- c.with_body do
- enable_service_ping_link_url = help_page_path('administration/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics')
- enable_service_ping_link = '<a href="%{url}">'.html_safe % { url: enable_service_ping_link_url }
- generate_manually_link_url = help_page_path('development/internal_analytics/service_ping/troubleshooting', anchor: 'generate-service-ping')
- generate_manually_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_manually_link_url }
- enable_service_ping_link = link_to('', help_page_path('administration/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics'), target: '_blank', rel: 'noopener noreferrer')
- generate_manually_link = link_to('', help_page_path('development/internal_analytics/service_ping/troubleshooting', anchor: 'generate-service-ping'), target: '_blank', rel: 'noopener noreferrer')
= html_escape(s_('%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload.')) % { enable_service_ping_link_start: enable_service_ping_link, generate_manually_link_start: generate_manually_link, link_end: '</a>'.html_safe }
= safe_format(s_('%{enable_service_ping_link_start}Enable%{enable_service_ping_link_end} or %{generate_manually_link_start}generate%{generate_manually_link_end} Service Ping to preview and download service usage data payload.'), tag_pair(enable_service_ping_link, :enable_service_ping_link_start, :enable_service_ping_link_end), tag_pair(generate_manually_link, :generate_manually_link_start, :generate_manually_link_end))

View File

@ -6,5 +6,5 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= render "ci/variables/content", entity: @entity, variable_limit: @variable_limit

View File

@ -11,7 +11,7 @@
= _('Naming, visibility')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= _('Collapse')
%p
%p.gl-text-secondary
= _('Update your group name, description, avatar, and visibility.')
= link_to _('Learn more about groups.'), help_page_path('user/group/index')
.settings-content
@ -23,7 +23,7 @@
= _('Permissions and group features')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= _('Configure advanced permissions, Large File Storage, two-factor authentication, and customer relations settings.')
.settings-content
= render 'groups/settings/permissions'
@ -38,7 +38,7 @@
= s_('GroupSettings|Badges')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p.gl-text-secondary.gl-mb-0
%p.gl-text-secondary
= s_('GroupSettings|Customize this group\'s badges.')
= link_to s_('GroupSettings|What are badges?'), help_page_path('user/project/badges')
.settings-content
@ -55,7 +55,7 @@
= _('Advanced')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= _('Perform advanced options such as changing path, transferring, exporting, or removing the group.')
.settings-content
= render 'groups/settings/advanced'

View File

@ -14,7 +14,7 @@
= _("General pipelines")
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= _("Customize your pipeline configuration.")
.settings-content
= render 'groups/settings/ci_cd/form', group: @group
@ -31,7 +31,7 @@
= _('Runners')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
= link_to s_('What is GitLab Runner?'), 'https://docs.gitlab.com/runner/', target: '_blank', rel: 'noopener noreferrer'
.settings-content
@ -43,7 +43,7 @@
= _('Auto DevOps')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
- auto_devops_url = help_page_path('topics/autodevops/index')
- quickstart_url = help_page_path('topics/autodevops/cloud_deployments/auto_devops_with_gke')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }

View File

@ -6,5 +6,5 @@
%h3= s_('Integrations|Group-level integration management')
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_("Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "</a>".html_safe }
%p.gl-text-secondary= s_("Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "</a>".html_safe }
= render 'shared/integrations/index', integrations: @integrations

View File

@ -4,7 +4,7 @@
= _('Default branch')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= s_('GroupSettings|Set the initial name and protections for the default branch of new repositories created in the group.')
.settings-content
= gitlab_ui_form_for @group, url: group_path(@group, anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|

View File

@ -5,7 +5,7 @@
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= s_('DeployTokens|Deploy tokens')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
%p.gl-text-secondary
= description
.settings-content
#new-deploy-token-alert

View File

@ -0,0 +1,8 @@
---
name: enable_exclusive_lease_double_lock_rw
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128083
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/421156
milestone: '16.3'
type: development
group: group::scalability
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url:
milestone: '16.0'
type: development
group: group::security policies
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: use_cluster_shared_state_for_exclusive_lease
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128083
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/421156
milestone: '16.3'
type: development
group: group::scalability
default_enabled: false

View File

@ -16,5 +16,6 @@ tier:
- free
- premium
- ultimate
performance_indicator_type: []
performance_indicator_type:
- customer_health_score
milestone: "<13.9"

View File

@ -17,5 +17,6 @@ tier:
- free
- premium
- ultimate
performance_indicator_type: []
performance_indicator_type:
- customer_health_score
milestone: "<13.9"

View File

@ -17,5 +17,6 @@ tier:
- free
- premium
- ultimate
performance_indicator_type: []
performance_indicator_type:
- customer_health_score
milestone: "<13.9"

View File

@ -21,3 +21,5 @@ tier:
- premium
- ultimate
milestone: "<13.9"
performance_indicator_type:
- customer_health_score

View File

@ -6,6 +6,9 @@ development:
cluster_cache:
cluster:
- redis://localhost:7001
cluster_shared_state:
cluster:
- redis://localhost:7001
feature_flag:
cluster:
- redis://localhost:7001
@ -20,6 +23,9 @@ test:
cluster_cache:
cluster:
- redis://localhost:7001
cluster_shared_state:
cluster:
- redis://localhost:7001
feature_flag:
cluster:
- redis://localhost:7001

View File

@ -26438,6 +26438,7 @@ Type of file the position refers to.
| Value | Description |
| ----- | ----------- |
| <a id="diffpositiontypefile"></a>`file` | Unknown file type. |
| <a id="diffpositiontypeimage"></a>`image` | An image. |
| <a id="diffpositiontypetext"></a>`text` | Text file. |

View File

@ -69,6 +69,7 @@ Prerequisites:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in GitLab 13.7 [with a flag](../../../administration/feature_flags.md) named `dependency_proxy_for_private_groups`. Enabled by default.
> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/276777) the feature flag `dependency_proxy_for_private_groups` in GitLab 15.0.
> - Support for group access tokens [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362991) in GitLab 16.3.
Because the Dependency Proxy is storing Docker images in a space associated with your group,
you must authenticate against the Dependency Proxy.
@ -87,6 +88,7 @@ You can authenticate using:
- Your GitLab username and password.
- A [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `read_registry` and `write_registry`.
- A [group deploy token](../../../user/project/deploy_tokens/index.md) with the scope set to `read_registry` and `write_registry`.
- A [group access token](../../../user/group/settings/group_access_tokens.md) for the group, with the scope set to `read_registry` and `write_registry`.
Users accessing the Dependency Proxy with a personal access token or username and password must
have at least the Guest role for the group they pull images from.

View File

@ -14,7 +14,7 @@ module BulkImports
<<-GRAPHQL
query($full_path: ID!, $cursor: String, $per_page: Int) {
portable: #{context.entity.entity_type}(fullPath: $full_path) {
members: #{members_type}(relations: [DIRECT, INHERITED], first: $per_page, after: $cursor) {
members: #{members_type}(relations: #{relations}, first: $per_page, after: $cursor) {
page_info: pageInfo {
next_page: endCursor
has_next_page: hasNextPage
@ -66,6 +66,14 @@ module BulkImports
'projectMembers'
end
end
def relations
if context.entity.group?
"[DIRECT INHERITED SHARED_FROM_GROUPS]"
else
"[DIRECT INHERITED INVITED_GROUPS SHARED_INTO_ANCESTORS]"
end
end
end
end
end

View File

@ -12,6 +12,8 @@ module Gitlab
# ExclusiveLease.
#
class ExclusiveLease
include Gitlab::Utils::StrongMemoize
PREFIX = 'gitlab:exclusive_lease'
NoKey = Class.new(ArgumentError)
@ -31,7 +33,7 @@ module Gitlab
EOS
def self.get_uuid(key)
Gitlab::Redis::SharedState.with do |redis|
with_read_redis do |redis|
redis.get(redis_shared_state_key(key)) || false
end
end
@ -61,7 +63,7 @@ module Gitlab
def self.cancel(key, uuid)
return unless key.present?
Gitlab::Redis::SharedState.with do |redis|
with_write_redis do |redis|
redis.eval(LUA_CANCEL_SCRIPT, keys: [ensure_prefixed_key(key)], argv: [uuid])
end
end
@ -84,6 +86,21 @@ module Gitlab
redis.del(key)
end
end
Gitlab::Redis::ClusterSharedState.with do |redis|
redis.scan_each(match: redis_shared_state_key(scope)).each do |key|
redis.del(key)
end
end
end
def self.use_cluster_shared_state?
Gitlab::SafeRequestStore[:use_cluster_shared_state] ||=
Feature.enabled?(:use_cluster_shared_state_for_exclusive_lease)
end
def self.use_double_lock?
Gitlab::SafeRequestStore[:use_double_lock] ||= Feature.enabled?(:enable_exclusive_lease_double_lock_rw)
end
def initialize(key, uuid: nil, timeout:)
@ -95,10 +112,23 @@ module Gitlab
# Try to obtain the lease. Return lease UUID on success,
# false if the lease is already taken.
def try_obtain
return try_obtain_with_new_lock if self.class.use_cluster_shared_state?
# Performing a single SET is atomic
Gitlab::Redis::SharedState.with do |redis|
redis.set(@redis_shared_state_key, @uuid, nx: true, ex: @timeout) && @uuid
end
obtained = set_lease(Gitlab::Redis::SharedState) && @uuid
# traffic to new store is minimal since only the first lock holder can run SETNX in ClusterSharedState
return false unless obtained
return obtained unless self.class.use_double_lock?
return obtained if same_store # 2nd setnx will surely fail if store are the same
second_lock_obtained = set_lease(Gitlab::Redis::ClusterSharedState) && @uuid
# cancel is safe since it deletes key only if value matches uuid
# i.e. it will not delete the held lock on ClusterSharedState
cancel unless second_lock_obtained
second_lock_obtained
end
# This lease is waiting to obtain
@ -109,7 +139,7 @@ module Gitlab
# Try to renew an existing lease. Return lease UUID on success,
# false if the lease is taken by a different UUID or inexistent.
def renew
Gitlab::Redis::SharedState.with do |redis|
self.class.with_write_redis do |redis|
result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_shared_state_key], argv: [@uuid, @timeout])
result == @uuid
end
@ -117,7 +147,7 @@ module Gitlab
# Returns true if the key for this lease is set.
def exists?
Gitlab::Redis::SharedState.with do |redis|
self.class.with_read_redis do |redis|
redis.exists?(@redis_shared_state_key) # rubocop:disable CodeReuse/ActiveRecord
end
end
@ -126,17 +156,66 @@ module Gitlab
#
# This method will return `nil` if no TTL could be obtained.
def ttl
Gitlab::Redis::SharedState.with do |redis|
self.class.with_read_redis do |redis|
ttl = redis.ttl(@redis_shared_state_key)
ttl if ttl > 0
end
end
# rubocop:disable CodeReuse/ActiveRecord
def self.with_write_redis(&blk)
if use_cluster_shared_state?
result = Gitlab::Redis::ClusterSharedState.with(&blk)
Gitlab::Redis::SharedState.with(&blk)
result
elsif use_double_lock?
result = Gitlab::Redis::SharedState.with(&blk)
Gitlab::Redis::ClusterSharedState.with(&blk)
result
else
Gitlab::Redis::SharedState.with(&blk)
end
end
def self.with_read_redis(&blk)
if use_cluster_shared_state?
Gitlab::Redis::ClusterSharedState.with(&blk)
elsif use_double_lock?
Gitlab::Redis::SharedState.with(&blk) || Gitlab::Redis::ClusterSharedState.with(&blk)
else
Gitlab::Redis::SharedState.with(&blk)
end
end
# rubocop:enable CodeReuse/ActiveRecord
# Gives up this lease, allowing it to be obtained by others.
def cancel
self.class.cancel(@redis_shared_state_key, @uuid)
end
private
def set_lease(redis_class)
redis_class.with do |redis|
redis.set(@redis_shared_state_key, @uuid, nx: true, ex: @timeout)
end
end
def try_obtain_with_new_lock
# checks shared-state to avoid 2 versions of the application acquiring 1 lock
# wait for held lock to expire or yielded in case any process on old version is running
return false if Gitlab::Redis::SharedState.with { |c| c.exists?(@redis_shared_state_key) } # rubocop:disable CodeReuse/ActiveRecord
set_lease(Gitlab::Redis::ClusterSharedState) && @uuid
end
def same_store
Gitlab::Redis::ClusterSharedState.with(&:id) == Gitlab::Redis::SharedState.with(&:id) # rubocop:disable CodeReuse/ActiveRecord
end
strong_memoize_attr :same_store
end
end

View File

@ -10,6 +10,7 @@ module Gitlab
ALL_CLASSES = [
Gitlab::Redis::Cache,
Gitlab::Redis::ClusterCache,
Gitlab::Redis::ClusterSharedState,
Gitlab::Redis::DbLoadBalancing,
Gitlab::Redis::FeatureFlag,
Gitlab::Redis::Queues,

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Gitlab
module Redis
class ClusterSharedState < ::Gitlab::Redis::Wrapper
class << self
def config_fallback
SharedState
end
end
end
end
end

View File

@ -718,7 +718,7 @@ msgstr ""
msgid "%{emailPrefix}@company.com"
msgstr ""
msgid "%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload."
msgid "%{enable_service_ping_link_start}Enable%{enable_service_ping_link_end} or %{generate_manually_link_start}generate%{generate_manually_link_end} Service Ping to preview and download service usage data payload."
msgstr ""
msgid "%{extra} more downstream pipelines"
@ -12282,10 +12282,10 @@ msgstr ""
msgid "Configure %{italic_start}What's new%{italic_end} drawer and content."
msgstr ""
msgid "Configure %{link} to track events. %{link_start}Learn more.%{link_end}"
msgid "Configure %{repository_checks_link_start}repository checks%{link_end} and %{housekeeping_link_start}housekeeping%{link_end} on repositories."
msgstr ""
msgid "Configure %{repository_checks_link_start}repository checks%{link_end} and %{housekeeping_link_start}housekeeping%{link_end} on repositories."
msgid "Configure %{snowplow_link_start}Snowplow%{snowplow_link_end} to track events. %{help_link_start}Learn more.%{help_link_end}"
msgstr ""
msgid "Configure CAPTCHAs, IP address limits, and other anti-spam measures."
@ -18057,7 +18057,7 @@ msgstr ""
msgid "Environment scope"
msgstr ""
msgid "Environment variables on this GitLab instance are configured to be %{link_start}protected%{link_end} by default."
msgid "Environment variables on this GitLab instance are configured to be %{help_link_start}protected%{help_link_end} by default."
msgstr ""
msgid "Environment:"
@ -21653,7 +21653,7 @@ msgstr ""
msgid "Gitpod|To use Gitpod you must first enable the feature in the integrations section of your %{linkStart}user preferences%{linkEnd}."
msgstr ""
msgid "Gitpod|To use the integration, each user must also enable Gitpod on their GitLab account. %{link_start}How do I enable it?%{link_end} "
msgid "Gitpod|To use the integration, each user must also enable Gitpod on their GitLab account. %{help_link_start}How do I enable it?%{help_link_end}"
msgstr ""
msgid "Gitpod|https://gitpod.example.com"
@ -22616,7 +22616,7 @@ msgstr ""
msgid "GroupSettings|Compliance frameworks"
msgstr ""
msgid "GroupSettings|Configure analytics features for this group"
msgid "GroupSettings|Configure analytics features for this group."
msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}What are compliance frameworks?%{linkEnd}"

View File

@ -68,7 +68,14 @@ module QA
package.remove_via_api!
end
it 'publishes a composer package and deletes it', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348016' do
it(
'publishes a composer package and deletes it',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348016',
quarantine: {
type: :broken,
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/421885"
}
) do
Page::Project::Menu.perform(&:go_to_package_registry)
Page::Project::Packages::Index.perform do |index|

View File

@ -17,93 +17,78 @@ RSpec.describe Groups::DependencyProxyAuthController do
end
end
context 'with valid JWT' do
context 'user' do
let_it_be(:user) { create(:user) }
context 'with JWT' do
let(:jwt) { build_jwt(user) }
let(:token_header) { "Bearer #{jwt.encoded}" }
let(:jwt) { build_jwt(user) }
let(:token_header) { "Bearer #{jwt.encoded}" }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
end
it { is_expected.to have_gitlab_http_status(:success) }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
end
context 'deploy token' do
let_it_be(:user) { create(:deploy_token) }
context 'with valid JWT' do
context 'user' do
let_it_be(:user) { create(:user) }
let(:jwt) { build_jwt(user) }
let(:token_header) { "Bearer #{jwt.encoded}" }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
it { is_expected.to have_gitlab_http_status(:success) }
end
it { is_expected.to have_gitlab_http_status(:success) }
end
end
context 'group bot user' do
let_it_be(:user) { create(:user, :project_bot) }
context 'with invalid JWT' do
context 'bad user' do
let(:jwt) { build_jwt(double('bad_user', id: 999)) }
let(:token_header) { "Bearer #{jwt.encoded}" }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
it { is_expected.to have_gitlab_http_status(:success) }
end
it { is_expected.to have_gitlab_http_status(:unauthorized) }
context 'deploy token' do
let_it_be(:user) { create(:deploy_token) }
it { is_expected.to have_gitlab_http_status(:success) }
end
end
context 'token with no user id' do
let(:token_header) { "Bearer #{build_jwt.encoded}" }
context 'with invalid JWT' do
context 'bad user' do
let(:jwt) { build_jwt(double('bad_user', id: 999)) }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
context 'token with no user id' do
let(:token_header) { "Bearer #{build_jwt.encoded}" }
context 'expired token' do
let_it_be(:user) { create(:user) }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
end
let(:jwt) { build_jwt(user, expire_time: Time.zone.now - 1.hour) }
let(:token_header) { "Bearer #{jwt.encoded}" }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
context 'expired token' do
let_it_be(:user) { create(:user) }
context 'expired deploy token' do
let_it_be(:user) { create(:deploy_token, :expired) }
let(:jwt) { build_jwt(user, expire_time: Time.zone.now - 1.hour) }
let(:jwt) { build_jwt(user) }
let(:token_header) { "Bearer #{jwt.encoded}" }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
context 'group bot user from an expired token' do
let_it_be(:user) { create(:user, :project_bot) }
context 'revoked deploy token' do
let_it_be(:user) { create(:deploy_token, :revoked) }
let(:jwt) { build_jwt(user, expire_time: Time.zone.now - 1.hour) }
let(:jwt) { build_jwt(user) }
let(:token_header) { "Bearer #{jwt.encoded}" }
before do
request.headers['HTTP_AUTHORIZATION'] = token_header
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
it { is_expected.to have_gitlab_http_status(:unauthorized) }
context 'expired deploy token' do
let_it_be(:user) { create(:deploy_token, :expired) }
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
context 'revoked deploy token' do
let_it_be(:user) { create(:deploy_token, :revoked) }
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe BulkImports::Common::Graphql::GetMembersQuery do
RSpec.describe BulkImports::Common::Graphql::GetMembersQuery, feature_category: :importers do
let(:entity) { create(:bulk_import_entity, :group_entity) }
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
@ -41,6 +41,7 @@ RSpec.describe BulkImports::Common::Graphql::GetMembersQuery do
it 'queries group & group members' do
expect(query.to_s).to include('group')
expect(query.to_s).to include('groupMembers')
expect(query.to_s).to include('SHARED_FROM_GROUPS')
end
end
@ -50,6 +51,7 @@ RSpec.describe BulkImports::Common::Graphql::GetMembersQuery do
it 'queries project & project members' do
expect(query.to_s).to include('project')
expect(query.to_s).to include('projectMembers')
expect(query.to_s).to include('INVITED_GROUPS SHARED_INTO_ANCESTORS')
end
end
end

View File

@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do
RSpec.describe Gitlab::ExclusiveLease, :request_store, :clean_gitlab_redis_shared_state,
:clean_gitlab_redis_cluster_shared_state, feature_category: :shared do
let(:unique_key) { SecureRandom.hex(10) }
describe '#try_obtain' do
@ -19,6 +20,67 @@ RSpec.describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do
sleep(2 * timeout) # lease should have expired now
expect(lease.try_obtain).to be_present
end
context 'when migrating across stores' do
let(:lease) { described_class.new(unique_key, timeout: 3600) }
before do
stub_feature_flags(use_cluster_shared_state_for_exclusive_lease: false)
allow(lease).to receive(:same_store).and_return(false)
end
it 'acquires 2 locks' do
# stub first SETNX
Gitlab::Redis::SharedState.with { |r| expect(r).to receive(:set).and_return(true) }
Gitlab::Redis::ClusterSharedState.with { |r| expect(r).to receive(:set).and_call_original }
expect(lease.try_obtain).to be_truthy
end
it 'rollback first lock if second lock is not acquired' do
Gitlab::Redis::ClusterSharedState.with do |r|
expect(r).to receive(:set).and_return(false)
expect(r).to receive(:eval).and_call_original
end
Gitlab::Redis::SharedState.with do |r|
expect(r).to receive(:set).and_call_original
expect(r).to receive(:eval).and_call_original
end
expect(lease.try_obtain).to be_falsey
end
end
context 'when cutting over to ClusterSharedState' do
context 'when lock is not acquired' do
it 'waits for existing holder to yield the lock' do
Gitlab::Redis::ClusterSharedState.with { |r| expect(r).to receive(:set).and_call_original }
Gitlab::Redis::SharedState.with { |r| expect(r).not_to receive(:set) }
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.try_obtain).to be_truthy
end
end
context 'when lock is still acquired' do
let(:lease) { described_class.new(unique_key, timeout: 3600) }
before do
# simulates cutover where some application's feature-flag has not updated
stub_feature_flags(use_cluster_shared_state_for_exclusive_lease: false)
lease.try_obtain
stub_feature_flags(use_cluster_shared_state_for_exclusive_lease: true)
end
it 'waits for existing holder to yield the lock' do
Gitlab::Redis::ClusterSharedState.with { |r| expect(r).not_to receive(:set) }
Gitlab::Redis::SharedState.with { |r| expect(r).not_to receive(:set) }
expect(lease.try_obtain).to be_falsey
end
end
end
end
describe '.redis_shared_state_key' do
@ -42,131 +104,159 @@ RSpec.describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do
end
end
describe '#renew' do
it 'returns true when we have the existing lease' do
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.try_obtain).to be_present
expect(lease.renew).to be_truthy
end
shared_examples 'write operations' do
describe '#renew' do
it 'returns true when we have the existing lease' do
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.try_obtain).to be_present
expect(lease.renew).to be_truthy
end
it 'returns false when we dont have a lease' do
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.renew).to be_falsey
end
end
describe '#exists?' do
it 'returns true for an existing lease' do
lease = described_class.new(unique_key, timeout: 3600)
lease.try_obtain
expect(lease.exists?).to eq(true)
end
it 'returns false for a lease that does not exist' do
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.exists?).to eq(false)
end
end
describe '.get_uuid' do
it 'gets the uuid if lease with the key associated exists' do
uuid = described_class.new(unique_key, timeout: 3600).try_obtain
expect(described_class.get_uuid(unique_key)).to eq(uuid)
end
it 'returns false if the lease does not exist' do
expect(described_class.get_uuid(unique_key)).to be false
end
end
describe 'cancellation' do
def new_lease(key)
described_class.new(key, timeout: 3600)
end
shared_examples 'cancelling a lease' do
let(:lease) { new_lease(unique_key) }
it 'releases the held lease' do
uuid = lease.try_obtain
expect(uuid).to be_present
expect(new_lease(unique_key).try_obtain).to eq(false)
cancel_lease(uuid)
expect(new_lease(unique_key).try_obtain).to be_present
it 'returns false when we dont have a lease' do
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.renew).to be_falsey
end
end
describe '.cancel' do
def cancel_lease(uuid)
described_class.cancel(release_key, uuid)
describe 'cancellation' do
def new_lease(key)
described_class.new(key, timeout: 3600)
end
context 'when called with the unprefixed key' do
it_behaves_like 'cancelling a lease' do
let(:release_key) { unique_key }
shared_examples 'cancelling a lease' do
let(:lease) { new_lease(unique_key) }
it 'releases the held lease' do
uuid = lease.try_obtain
expect(uuid).to be_present
expect(new_lease(unique_key).try_obtain).to eq(false)
cancel_lease(uuid)
expect(new_lease(unique_key).try_obtain).to be_present
end
end
context 'when called with the prefixed key' do
it_behaves_like 'cancelling a lease' do
let(:release_key) { described_class.redis_shared_state_key(unique_key) }
describe '.cancel' do
def cancel_lease(uuid)
described_class.cancel(release_key, uuid)
end
context 'when called with the unprefixed key' do
it_behaves_like 'cancelling a lease' do
let(:release_key) { unique_key }
end
end
context 'when called with the prefixed key' do
it_behaves_like 'cancelling a lease' do
let(:release_key) { described_class.redis_shared_state_key(unique_key) }
end
end
it 'does not raise errors when given a nil key' do
expect { described_class.cancel(nil, nil) }.not_to raise_error
end
end
it 'does not raise errors when given a nil key' do
expect { described_class.cancel(nil, nil) }.not_to raise_error
describe '#cancel' do
def cancel_lease(_uuid)
lease.cancel
end
it_behaves_like 'cancelling a lease'
it 'is safe to call even if the lease was never obtained' do
lease = new_lease(unique_key)
lease.cancel
expect(new_lease(unique_key).try_obtain).to be_present
end
end
end
describe '#cancel' do
def cancel_lease(_uuid)
lease.cancel
end
describe '.reset_all!' do
it 'removes all existing lease keys from redis' do
uuid = described_class.new(unique_key, timeout: 3600).try_obtain
it_behaves_like 'cancelling a lease'
expect(described_class.get_uuid(unique_key)).to eq(uuid)
it 'is safe to call even if the lease was never obtained' do
lease = new_lease(unique_key)
described_class.reset_all!
lease.cancel
expect(new_lease(unique_key).try_obtain).to be_present
expect(described_class.get_uuid(unique_key)).to be_falsey
end
end
end
describe '#ttl' do
it 'returns the TTL of the Redis key' do
lease = described_class.new('kittens', timeout: 100)
lease.try_obtain
shared_examples 'read operations' do
describe '#exists?' do
it 'returns true for an existing lease' do
lease = described_class.new(unique_key, timeout: 3600)
lease.try_obtain
expect(lease.ttl <= 100).to eq(true)
expect(lease.exists?).to eq(true)
end
it 'returns false for a lease that does not exist' do
lease = described_class.new(unique_key, timeout: 3600)
expect(lease.exists?).to eq(false)
end
end
it 'returns nil when the lease does not exist' do
lease = described_class.new('kittens', timeout: 10)
describe '.get_uuid' do
it 'gets the uuid if lease with the key associated exists' do
uuid = described_class.new(unique_key, timeout: 3600).try_obtain
expect(lease.ttl).to be_nil
expect(described_class.get_uuid(unique_key)).to eq(uuid)
end
it 'returns false if the lease does not exist' do
expect(described_class.get_uuid(unique_key)).to be false
end
end
describe '#ttl' do
it 'returns the TTL of the Redis key' do
lease = described_class.new('kittens', timeout: 100)
lease.try_obtain
expect(lease.ttl <= 100).to eq(true)
end
it 'returns nil when the lease does not exist' do
lease = described_class.new('kittens', timeout: 10)
expect(lease.ttl).to be_nil
end
end
end
describe '.reset_all!' do
it 'removes all existing lease keys from redis' do
uuid = described_class.new(unique_key, timeout: 3600).try_obtain
expect(described_class.get_uuid(unique_key)).to eq(uuid)
described_class.reset_all!
expect(described_class.get_uuid(unique_key)).to be_falsey
context 'when migrating across stores' do
before do
stub_feature_flags(use_cluster_shared_state_for_exclusive_lease: false)
end
it_behaves_like 'read operations'
it_behaves_like 'write operations'
end
context 'when feature flags are all disabled' do
before do
stub_feature_flags(
use_cluster_shared_state_for_exclusive_lease: false,
enable_exclusive_lease_double_lock_rw: false
)
end
it_behaves_like 'read operations'
it_behaves_like 'write operations'
end
it_behaves_like 'read operations'
it_behaves_like 'write operations'
describe '.throttle' do
it 'prevents repeated execution of the block' do
number = 0
@ -244,4 +334,74 @@ RSpec.describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do
described_class.throttle(1, count: 48, period: 1.day) {}
end
end
describe 'transitions between feature-flag toggles' do
shared_examples 'retains behaviours across transitions' do |flag|
it 'retains read behaviour' do
lease = described_class.new(unique_key, timeout: 3600)
uuid = lease.try_obtain
expect(lease.ttl).not_to eq(nil)
expect(lease.exists?).to be_truthy
expect(described_class.get_uuid(unique_key)).to eq(uuid)
# simulates transition
stub_feature_flags({ flag => true })
Gitlab::SafeRequestStore.clear!
expect(lease.ttl).not_to eq(nil)
expect(lease.exists?).to be_truthy
expect(described_class.get_uuid(unique_key)).to eq(uuid)
end
it 'retains renew behaviour' do
lease = described_class.new(unique_key, timeout: 3600)
lease.try_obtain
expect(lease.renew).to be_truthy
# simulates transition
stub_feature_flags({ flag => true })
Gitlab::SafeRequestStore.clear!
expect(lease.renew).to be_truthy
end
it 'retains renew behaviour' do
lease = described_class.new(unique_key, timeout: 3600)
uuid = lease.try_obtain
lease.cancel
# proves successful cancellation
expect(lease.try_obtain).to eq(uuid)
# simulates transition
stub_feature_flags({ flag => true })
Gitlab::SafeRequestStore.clear!
expect(lease.try_obtain).to be_falsey
lease.cancel
expect(lease.try_obtain).to eq(uuid)
end
end
context 'when enabling enable_exclusive_lease_double_lock_rw' do
before do
stub_feature_flags(
enable_exclusive_lease_double_lock_rw: false,
use_cluster_shared_state_for_exclusive_lease: false
)
end
it_behaves_like 'retains behaviours across transitions', :enable_exclusive_lease_double_lock_rw
end
context 'when enabling use_cluster_shared_state_for_exclusive_lease' do
before do
stub_feature_flags(use_cluster_shared_state_for_exclusive_lease: false)
end
it_behaves_like 'retains behaviours across transitions', :use_cluster_shared_state_for_exclusive_lease
end
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Redis::ClusterSharedState, feature_category: :redis do
include_examples "redis_new_instance_shared_examples", 'cluster_shared_state', Gitlab::Redis::SharedState
end

View File

@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe BackgroundMigration::CiDatabaseWorker, :clean_gitlab_redis_shared_state, feature_category: :database do
RSpec.describe BackgroundMigration::CiDatabaseWorker, :clean_gitlab_redis_shared_state,
:clean_gitlab_redis_cluster_shared_state, feature_category: :database do
before do
skip_if_shared_database(:ci)
end

View File

@ -2,6 +2,7 @@
require 'spec_helper'
RSpec.describe BackgroundMigrationWorker, :clean_gitlab_redis_shared_state, feature_category: :database do
RSpec.describe BackgroundMigrationWorker, :clean_gitlab_redis_shared_state,
:clean_gitlab_redis_cluster_shared_state, feature_category: :database do
it_behaves_like 'it runs background migration jobs', 'main'
end

View File

@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Integrations::SlackEventWorker, :clean_gitlab_redis_shared_state, feature_category: :integrations do
RSpec.describe Integrations::SlackEventWorker, :clean_gitlab_redis_shared_state,
:clean_gitlab_redis_cluster_shared_state, feature_category: :integrations do
describe '.event?' do
subject { described_class.event?(event) }

View File

@ -1 +1 @@
golang 1.20.6
golang 1.20.7