Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-02-08 18:11:25 +00:00
parent 89db068a0a
commit 0b84d7017e
159 changed files with 1669 additions and 902 deletions

View File

@ -275,6 +275,14 @@ Dangerfile
/ee/spec/services/software_license_policies/**
/spec/finders/security/license_compliance_jobs_finder_spec.rb
^[Secure::Static Analysis] @gitlab-org/secure/static-analysis
/ee/lib/gitlab/checks/secrets_check.rb
/ee/spec/lib/gitlab/checks/secrets_check_spec.rb
/ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb
/ee/spec/support/shared_examples/lib/gitlab/secrets_check_shared_examples.rb
/lib/gitlab/checks/changed_blobs.rb
/spec/lib/gitlab/checks/changed_blobs.rb
^[Code Owners] @reprazent @kerrizor @garyh
/ee/lib/gitlab/code_owners.rb
/ee/lib/gitlab/code_owners/

View File

@ -3703,6 +3703,7 @@ Layout/LineLength:
- 'spec/lib/gitlab/import_export/uploads_manager_spec.rb'
- 'spec/lib/gitlab/import_export/version_checker_spec.rb'
- 'spec/lib/gitlab/import_sources_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
- 'spec/lib/gitlab/issuable_metadata_spec.rb'
- 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
- 'spec/lib/gitlab/jira/dvcs_spec.rb'

View File

@ -102,6 +102,7 @@ Lint/AmbiguousOperatorPrecedence:
- 'spec/lib/gitlab/database/batch_count_spec.rb'
- 'spec/lib/gitlab/database/consistency_checker_spec.rb'
- 'spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
- 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
- 'spec/lib/gitlab/kroki_spec.rb'
- 'spec/lib/gitlab/memory/instrumentation_spec.rb'

View File

@ -3587,6 +3587,7 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_base_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_spec.rb'
- 'spec/lib/gitlab/internal_post_receive/response_spec.rb'
- 'spec/lib/gitlab/issuable/clone/attributes_rewriter_spec.rb'
@ -5323,7 +5324,6 @@ RSpec/FeatureCategory:
- 'spec/views/layouts/devise_empty.html.haml_spec.rb'
- 'spec/views/layouts/fullscreen.html.haml_spec.rb'
- 'spec/views/layouts/profile.html.haml_spec.rb'
- 'spec/views/layouts/signup_onboarding.html.haml_spec.rb'
- 'spec/views/layouts/terms.html.haml_spec.rb'
- 'spec/views/notify/approved_merge_request_email.html.haml_spec.rb'
- 'spec/views/notify/autodevops_disabled_email.text.erb_spec.rb'

View File

@ -2332,6 +2332,7 @@ RSpec/NamedSubject:
- 'spec/lib/gitlab/rack_attack_spec.rb'
- 'spec/lib/gitlab/reactive_cache_set_cache_spec.rb'
- 'spec/lib/gitlab/redis/boolean_spec.rb'
- 'spec/lib/gitlab/redis/cross_slot_spec.rb'
- 'spec/lib/gitlab/redis/db_load_balancing_spec.rb'
- 'spec/lib/gitlab/redis/multi_store_spec.rb'
- 'spec/lib/gitlab/redis/queues_spec.rb'

View File

@ -2517,6 +2517,7 @@ Style/InlineDisableAnnotation:
- 'lib/gitlab/import_export/project/relation_factory.rb'
- 'lib/gitlab/import_sources.rb'
- 'lib/gitlab/instrumentation/redis_cluster_validator.rb'
- 'lib/gitlab/instrumentation/redis_interceptor.rb'
- 'lib/gitlab/internal_events.rb'
- 'lib/gitlab/issuable/clone/copy_resource_events_service.rb'
- 'lib/gitlab/issues/rebalancing/state.rb'
@ -2555,6 +2556,7 @@ Style/InlineDisableAnnotation:
- 'lib/gitlab/pagination/offset_pagination.rb'
- 'lib/gitlab/pagination_delegate.rb'
- 'lib/gitlab/patch/action_cable_subscription_adapter_identifier.rb'
- 'lib/gitlab/patch/node_loader.rb'
- 'lib/gitlab/patch/prependable.rb'
- 'lib/gitlab/patch/redis_cache_store.rb'
- 'lib/gitlab/patch/sidekiq_cron_poller.rb'
@ -2571,6 +2573,7 @@ Style/InlineDisableAnnotation:
- 'lib/gitlab/rack_attack.rb'
- 'lib/gitlab/rack_attack/request.rb'
- 'lib/gitlab/rack_attack/store.rb'
- 'lib/gitlab/redis/cross_slot.rb'
- 'lib/gitlab/redis/hll.rb'
- 'lib/gitlab/redis/multi_store.rb'
- 'lib/gitlab/reference_extractor.rb'
@ -2939,7 +2942,9 @@ Style/InlineDisableAnnotation:
- 'spec/lib/gitlab/pagination/keyset/order_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb'
- 'spec/lib/gitlab/patch/database_config_spec.rb'
- 'spec/lib/gitlab/patch/node_loader_spec.rb'
- 'spec/lib/gitlab/quick_actions/dsl_spec.rb'
- 'spec/lib/gitlab/redis/cross_slot_spec.rb'
- 'spec/lib/gitlab/redis/multi_store_spec.rb'
- 'spec/lib/gitlab/search/abuse_detection_spec.rb'
- 'spec/lib/gitlab/shard_health_cache_spec.rb'

View File

@ -288,9 +288,8 @@ gem 'js_regex', '~> 3.8' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'device_detector' # rubocop:todo Gemfile/MissingFeatureCategory
# Redis
gem 'redis-namespace', '~> 1.10.0', feature_category: :redis
gem 'redis', '~> 5.0.0', feature_category: :redis
gem 'redis-clustering', '~> 5.0.0', feature_category: :redis
gem 'redis', '~> 4.8.0' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'redis-namespace', '~> 1.10.0' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'connection_pool', '~> 2.4' # rubocop:todo Gemfile/MissingFeatureCategory
# Redis session store
@ -512,7 +511,7 @@ group :test do
gem 'shoulda-matchers', '~> 5.1.0', require: false # rubocop:todo Gemfile/MissingFeatureCategory
gem 'email_spec', '~> 2.2.0' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'webmock', '~> 3.19.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'webmock', '~> 3.20.0' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'rails-controller-testing' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'concurrent-ruby', '~> 1.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'test-prof', '~> 1.3.1' # rubocop:todo Gemfile/MissingFeatureCategory

View File

@ -523,11 +523,9 @@
{"name":"recaptcha","version":"5.12.3","platform":"ruby","checksum":"37d1894add9e70a54d0c6c7f0ecbeedffbfa7d075acfbd4c509818dfdebdb7ee"},
{"name":"recursive-open-struct","version":"1.1.3","platform":"ruby","checksum":"a3538a72552fcebcd0ada657bdff313641a4a5fbc482c08cfb9a65acb1c9de5a"},
{"name":"redcarpet","version":"3.6.0","platform":"ruby","checksum":"8ad1889c0355ff4c47174af14edd06d62f45a326da1da6e8a121d59bdcd2e9e9"},
{"name":"redis","version":"5.0.8","platform":"ruby","checksum":"3b770ea597850b26d6a9718fa184241e53e6c8a7ae0486ee8bfaefd29f26f3d8"},
{"name":"redis","version":"4.8.0","platform":"ruby","checksum":"2000cf5014669c9dc821704b6d322a35a9a33852a95208911d9175d63b448a44"},
{"name":"redis-actionpack","version":"5.4.0","platform":"ruby","checksum":"f10cf649ab05914716d63334d7f709221ecc883b87cf348f90ecfe0c35ea3540"},
{"name":"redis-client","version":"0.19.0","platform":"ruby","checksum":"6ed9af23ff5aa87cf4d59439db77082b4cae5a0abbdd114ec5420bd63456324d"},
{"name":"redis-cluster-client","version":"0.7.5","platform":"ruby","checksum":"12fd1c9eda17157a5cd2ce46afba13a024c28d24922092299a8daa9f46e4e78a"},
{"name":"redis-clustering","version":"5.0.8","platform":"ruby","checksum":"8e2f3de3b1a700668eeac59125636e01be6ecd985e635a4d5649c47d71f6e166"},
{"name":"redis-namespace","version":"1.10.0","platform":"ruby","checksum":"2c1c6ea7c6c5e343e75b9bee3aa4c265e364a5b9966507397467af2bb3758d94"},
{"name":"redis-rack","version":"3.0.0","platform":"ruby","checksum":"abb50b82ae10ad4d11ca2e4901bfc2b98256cdafbbd95f80c86fc9e001478380"},
{"name":"redis-store","version":"1.10.0","platform":"ruby","checksum":"f258894f9f7e82834308a3d86242294f0cff2c9db9ae66e5cb4c553a5ec8b09e"},
@ -710,7 +708,7 @@
{"name":"warning","version":"1.3.0","platform":"ruby","checksum":"23695a5d8e50bd5c46068931b529bee0b28e4982cbcefbe77d867800dde8069e"},
{"name":"webauthn","version":"3.0.0","platform":"ruby","checksum":"3f77d422c2a8a4b31e56cf42f83414bd066e0506e9896936e1730262dc4a20e6"},
{"name":"webfinger","version":"1.2.0","platform":"ruby","checksum":"7814ef1c85da47514f65c6e5ca14205fa9ce41ea2a70785e0c872842162852a2"},
{"name":"webmock","version":"3.19.1","platform":"ruby","checksum":"eae7eee33989478188451f1fda4224d7fbe097c5c14e96b40b57347ef2d5d16d"},
{"name":"webmock","version":"3.20.0","platform":"ruby","checksum":"cc33eaacc0fcfb5b36a127949557ab7089d4249b03a7cb0215dbc40e617a8365"},
{"name":"webrick","version":"1.8.1","platform":"ruby","checksum":"19411ec6912911fd3df13559110127ea2badd0c035f7762873f58afc803e158f"},
{"name":"websocket","version":"1.2.10","platform":"ruby","checksum":"2cc1a4a79b6e63637b326b4273e46adcddf7871caa5dc5711f2ca4061a629fa8"},
{"name":"websocket-driver","version":"0.7.6","platform":"java","checksum":"bc894b9e9d5aee55ac04b61003e1957c4ef411a5a048199587d0499785b505c3"},

View File

@ -1385,19 +1385,13 @@ GEM
json
recursive-open-struct (1.1.3)
redcarpet (3.6.0)
redis (5.0.8)
redis-client (>= 0.17.0)
redis (4.8.0)
redis-actionpack (5.4.0)
actionpack (>= 5, < 8)
redis-rack (>= 2.1.0, < 4)
redis-store (>= 1.1.0, < 2)
redis-client (0.19.0)
connection_pool
redis-cluster-client (0.7.5)
redis-client (~> 0.12)
redis-clustering (5.0.8)
redis (= 5.0.8)
redis-cluster-client (>= 0.7.0)
redis-namespace (1.10.0)
redis (>= 4)
redis-rack (3.0.0)
@ -1796,7 +1790,7 @@ GEM
webfinger (1.2.0)
activesupport
httpclient (>= 2.4)
webmock (3.19.1)
webmock (3.20.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@ -2075,9 +2069,8 @@ DEPENDENCIES
rbtrace (~> 0.4)
re2 (= 2.7.0)
recaptcha (~> 5.12)
redis (~> 5.0.0)
redis (~> 4.8.0)
redis-actionpack (~> 5.4.0)
redis-clustering (~> 5.0.0)
redis-namespace (~> 1.10.0)
request_store (~> 1.5.1)
responders (~> 3.0)
@ -2153,7 +2146,7 @@ DEPENDENCIES
vmstat (~> 2.3.0)
warning (~> 1.3.0)
webauthn (~> 3.0)
webmock (~> 3.19.1)
webmock (~> 3.20.0)
webrick (~> 1.8.1)
wikicloth (= 0.8.1)
yajl-ruby (~> 1.4.3)

View File

@ -34,7 +34,7 @@ export const emptyStateIllustration = (state) => state?.job?.status?.illustratio
export const emptyStateAction = (state) => state?.job?.status?.action || null;
/**
* Shared runners limit is only rendered when
* Instance/shared runners limit is only rendered when
* used quota is bigger or equal than the limit
*
* @returns {Boolean}

View File

@ -127,13 +127,13 @@ export default {
:value="sharedRunnersToggleValue"
:is-loading="isLoading"
:disabled="isSharedRunnersToggleDisabled"
:label="__('Enable shared runners for this group')"
:description="__('Enable shared runners for all projects and subgroups in this group.')"
:label="__('Enable instance runners for this group')"
:description="__('Enable instance runners for all projects and subgroups in this group.')"
data-testid="shared-runners-toggle"
@change="onSharedRunnersToggle"
>
<template v-if="isSharedRunnersToggleDisabled" #help>
{{ s__('Runners|Shared runners are disabled.') }}
{{ s__('Runners|Instance runners are disabled.') }}
<gl-sprintf
v-if="isParentAvailable"
:message="s__('Runners|Go to %{groupLink} to enable them.')"
@ -159,7 +159,7 @@ export default {
@change="onOverrideToggle"
>
<template v-if="isSharedRunnersToggleDisabled" #help>
{{ s__('Runners|Shared runners are disabled.') }}
{{ s__('Runners|Instance runners are disabled.') }}
<gl-sprintf
v-if="isParentAvailable"
:message="s__('Runners|Go to %{groupLink} to enable them.')"

View File

@ -1,12 +1,12 @@
import { __, s__ } from '~/locale';
export const I18N_CONFIRM_MESSAGE = s__(
'Runners|Shared runners will be disabled for all projects and subgroups in this group.',
'Runners|Instance runners will be disabled for all projects and subgroups in this group.',
);
export const I18N_CONFIRM_OK = s__('Runners|Yes, disable shared runners');
export const I18N_CONFIRM_CANCEL = s__('Runners|No, keep shared runners enabled');
export const I18N_CONFIRM_OK = s__('Runners|Yes, disable instance runners');
export const I18N_CONFIRM_CANCEL = s__('Runners|No, keep instance runners enabled');
export const I18N_CONFIRM_TITLE = s__(
'Runners|Are you sure you want to disable shared runners for %{groupName}?',
'Runners|Are you sure you want to disable instance runners for %{groupName}?',
);
export const I18N_UPDATE_ERROR_MESSAGE = __('An error occurred while updating configuration.');

View File

@ -6,7 +6,7 @@ import { CC_VALIDATION_REQUIRED_ERROR } from '../constants';
const DEFAULT_ERROR_MESSAGE = __('An error occurred while updating the configuration.');
const REQUIRES_VALIDATION_TEXT = s__(
`Billings|Shared runners cannot be enabled until a valid credit card is on file.`,
`Billings|Instance runners cannot be enabled until a valid credit card is on file.`,
);
export default {
@ -122,13 +122,13 @@ export default {
ref="sharedRunnersToggle"
:disabled="isDisabledAndUnoverridable"
:is-loading="isLoading"
:label="__('Enable shared runners for this project')"
:label="__('Enable instance runners for this project')"
:value="isSharedRunnerEnabled"
data-testid="toggle-shared-runners"
@change="toggleSharedRunners"
>
<template v-if="isDisabledAndUnoverridable" #help>
{{ s__('Runners|Shared runners are disabled in the group settings.') }}
{{ s__('Runners|Instance runners are disabled in the group settings.') }}
<gl-sprintf
v-if="isGroupSettingsAvailable"
:message="s__('Runners|Go to %{groupLink} to enable them.')"

View File

@ -17,5 +17,5 @@ export const ACCESS_LEVEL_NONE = 0;
// must match shared_runners_setting in update_service.rb
export const CC_VALIDATION_REQUIRED_ERROR = __(
'Shared runners enabled cannot be enabled until a valid credit card is on file',
'Instance runners enabled cannot be enabled until a valid credit card is on file',
);

View File

@ -442,3 +442,11 @@
color: $gl-text-color;
}
}
@mixin devise-errors {
h2 {
margin-top: 0;
font-size: $gl-font-size;
color: var(--red-700, $red-700);
}
}

View File

@ -1,5 +1,23 @@
@import 'mixins_and_variables_and_functions';
.html-devise-layout {
margin: 0;
padding: 0;
height: 100%;
body {
&.with-system-header {
padding-top: $system-header-height;
}
&.with-system-footer {
.footer-container {
padding-bottom: $system-footer-height;
}
}
}
}
/* Login Page */
.login-page {
.container {
@ -42,30 +60,9 @@
}
}
.devise-errors {
h2 {
margin-top: 0;
font-size: 14px;
color: $red-700;
}
}
}
.html-devise-layout {
margin: 0;
padding: 0;
height: 100%;
body {
&.with-system-header {
padding-top: $system-header-height;
}
&.with-system-footer {
.footer-container {
padding-bottom: $system-footer-height;
}
}
@include devise-errors;
}
}

View File

@ -1,13 +1,6 @@
@import 'mixins_and_variables_and_functions';
.signup-page {
.devise-errors {
h2 {
font-size: $gl-font-size;
color: var(--red-700, $red-700);
}
}
.decline-page {
width: 350px;
}
@ -16,3 +9,7 @@
.edit-profile {
max-width: 460px;
}
.devise-errors {
@include devise-errors;
}

View File

@ -29,7 +29,7 @@ class InvitesController < ApplicationController
def decline
if member.decline_invite!
return render layout: 'signup_onboarding' if !current_user && member.invite_to_unknown_user? && member.created_by
return render layout: 'minimal' if !current_user && member.invite_to_unknown_user? && member.created_by
path =
if current_user

View File

@ -16,8 +16,14 @@ module Mutations
argument :full_path, GraphQL::Types::ID,
required: true,
description: 'Full path of the group that will be updated.'
argument :lock_math_rendering_limits_enabled, GraphQL::Types::Boolean,
required: false,
description: copy_field_description(Types::GroupType, :lock_math_rendering_limits_enabled)
argument :math_rendering_limits_enabled, GraphQL::Types::Boolean,
required: false,
description: copy_field_description(Types::GroupType, :math_rendering_limits_enabled)
argument :shared_runners_setting, Types::Namespace::SharedRunnersSettingEnum,
required: true,
required: false,
description: copy_field_description(Types::GroupType, :shared_runners_setting)
def resolve(full_path:, **args)

View File

@ -311,6 +311,18 @@ module Types
resolver: Resolvers::AutocompleteUsersResolver,
description: 'Search users for autocompletion'
field :lock_math_rendering_limits_enabled,
GraphQL::Types::Boolean,
null: true,
method: :lock_math_rendering_limits_enabled?,
description: 'Indicates if math rendering limits are locked for all descendant groups.'
field :math_rendering_limits_enabled,
GraphQL::Types::Boolean,
null: true,
method: :math_rendering_limits_enabled?,
description: 'Indicates if math rendering limits are used for this group.'
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
LabelsFinder

View File

@ -84,7 +84,7 @@ class ActiveSession
)
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
pipeline.setex(
key_name(user.id, session_private_id),
expiry,

View File

@ -157,6 +157,12 @@ class Namespace < ApplicationRecord
:npm_package_requests_forwarding,
to: :package_settings
delegate :default_branch_protection_defaults, to: :namespace_settings, allow_nil: true
delegate :math_rendering_limits_enabled,
:lock_math_rendering_limits_enabled,
to: :namespace_settings, allow_nil: true
delegate :math_rendering_limits_enabled?,
:lock_math_rendering_limits_enabled?,
to: :namespace_settings
before_save :update_new_emails_created_column, if: -> { emails_disabled_changed? }
before_create :sync_share_with_group_lock_with_parent

View File

@ -12,6 +12,7 @@ class NamespaceSetting < ApplicationRecord
cascading_attr :toggle_security_policy_custom_ci
cascading_attr :toggle_security_policies_policy_scope
cascading_attr :math_rendering_limits_enabled
belongs_to :namespace, inverse_of: :namespace_settings
@ -50,6 +51,8 @@ class NamespaceSetting < ApplicationRecord
subgroup_runner_token_expiration_interval
project_runner_token_expiration_interval
default_branch_protection_defaults
math_rendering_limits_enabled
lock_math_rendering_limits_enabled
].freeze
# matches the size set in the database constraint

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module BranchRules
class BaseService
include Gitlab::Allowable
attr_accessor :project, :branch_rule, :current_user
def initialize(branch_rule, user = nil)
@branch_rule = branch_rule
@project = branch_rule.project
@current_user = user
end
end
end
BranchRules::BaseService.prepend_mod

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
module BranchRules
class DestroyService < BaseService
def execute
raise Gitlab::Access::AccessDeniedError unless can_destroy_branch_rule?
return destroy_protected_branch if branch_rule.instance_of?(Projects::BranchRule)
yield if block_given?
ServiceResponse.error(message: 'Unknown branch rule type.')
end
private
def can_destroy_branch_rule?
can?(current_user, :destroy_protected_branch, branch_rule)
end
def destroy_protected_branch
service = ProtectedBranches::DestroyService.new(project, current_user)
return ServiceResponse.success if service.execute(branch_rule.protected_branch)
ServiceResponse.error(message: 'Failed to delete branch rule.')
end
end
end
BranchRules::DestroyService.prepend_mod

View File

@ -74,6 +74,8 @@ module Groups
end
params.delete(:allow_mfa_for_subgroups)
params.delete(:math_rendering_limits_enabled)
params.delete(:lock_math_rendering_limits_enabled)
end
def create_chat_team?

View File

@ -141,6 +141,11 @@ module Groups
params.delete(:default_branch_protection)
params.delete(:default_branch_protection_defaults)
end
unless can?(current_user, :admin_namespace, group)
params.delete(:math_rendering_limits_enabled)
params.delete(:lock_math_rendering_limits_enabled)
end
end
def handle_changes

View File

@ -15,14 +15,14 @@
= link_to _('Learn more.'), help_page_path('topics/autodevops/stages', anchor: 'auto-review-apps'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.gitlab_ui_checkbox_component :shared_runners_enabled, s_("AdminSettings|Enable shared runners for new projects"), help_text: s_("AdminSettings|All new projects can use the instance's shared runners by default.")
= f.gitlab_ui_checkbox_component :shared_runners_enabled, s_("AdminSettings|Enable instance runners for new projects"), help_text: s_("AdminSettings|All new projects can use instance runners by default.")
= render_if_exists 'admin/application_settings/shared_runners_minutes_setting', form: f
.form-group
= f.label :shared_runners_text, _('Shared runners details'), class: 'label-bold'
= f.label :shared_runners_text, s_('AdminSettings|Instance runners details'), class: 'label-bold'
= f.text_area :shared_runners_text, class: 'form-control gl-form-input', rows: 4
.form-text.text-muted= _("Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported.")
.form-text.text-muted= s_('AdminSettings|Add a custom message with details about instance runners. The message is visible when you view runners for projects and groups. Markdown is supported.')
.form-group
= f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold'
= f.number_field :max_artifacts_size, class: 'form-control gl-form-input'

View File

@ -21,7 +21,7 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
%p.gl-text-secondary
= _('Customize CI/CD settings, including Auto DevOps, shared runners, and job artifacts.')
= _('Customize CI/CD settings, including Auto DevOps, instance runners, and job artifacts.')
= render 'ci_cd'
= render_if_exists 'admin/application_settings/required_instance_ci_setting', expanded: expanded_by_default?

View File

@ -117,7 +117,7 @@
enabled: Gitlab.config.pages.enabled,
doc_href: help_instance_configuration_url)
= feature_entry(_('Shared Runners'),
= feature_entry(_('Instance Runners'),
href: admin_runners_path,
enabled: Gitlab.config.gitlab_ci.shared_runners_enabled)
.col-md-4.gl-mb-6

View File

@ -46,5 +46,5 @@
= f.fields_for :credit_card_validation do |ff|
= ff.gitlab_ui_checkbox_component :credit_card_validated_at,
s_('AdminUsers|Validate user account'),
help_text: s_('AdminUsers|A user can validate themselves by inputting a credit/debit card, or an admin can manually validate a user. Validated users can use free CI minutes on shared runners.'),
help_text: s_('AdminUsers|A user can validate themselves by inputting a credit/debit card, or an admin can manually validate a user. Validated users can use free CI minutes on instance runners.'),
checkbox_options: { checked: @user.credit_card_validated_at.present? }

View File

@ -1,5 +1,6 @@
- add_page_specific_style 'page_bundles/login'
- custom_text = custom_sign_in_description
!!! 5
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
= render "layouts/head"

View File

@ -1,14 +0,0 @@
- add_page_specific_style 'page_bundles/signup'
- add_page_specific_style 'page_bundles/login'
!!! 5
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
= render "layouts/head"
%body.signup-page{ class: "#{system_message_class} #{client_class_list}", data: body_data }
= header_message
= render "layouts/init_client_detection_flags"
= render "layouts/header/logo_with_title"
.container.gl-align-self-center
.content
= yield
= footer_message

View File

@ -43,10 +43,8 @@
.add-review-item-modal-trigger{ data: { context_commits_empty: 'true' } }
- if commits.size == 0 && context_commits.nil?
.commits-empty.gl-mt-6
.svg-content.svg-150
= image_tag('illustrations/empty-state/empty-search-md.svg')
%h4
= _('Your search didn\'t match any commits.')
%p
= render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-search-md.svg',
title: _('Your search didn\'t match any commits.')) do |c|
- c.with_description do
= _('Try changing or removing filters.')

View File

@ -3,11 +3,11 @@
#toggle-shared-runners-form{ data: toggle_shared_runners_settings_data(@project) }
- if @shared_runners_count == 0
= _('This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area.')
= _('This GitLab instance does not provide any instance runners yet. Administrators can register instance runners in the admin area.')
- else
%div{ data: { testid: 'available-shared-runners' } }
%h5.gl-mt-6.gl-mb-0
= s_('Runners|Available shared runners: %{count}') % {count: @shared_runners_count}
= s_('Runners|Available instance runners: %{count}') % {count: @shared_runners_count}
%ul.bordered-list
= render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
= paginate @shared_runners, theme: "gitlab", param_name: "shared_runners_page", params: { expand_runners: true, anchor: 'js-runners-settings' }

View File

@ -48,7 +48,7 @@
%strong
= s_('JiraService|Using Jira for issue tracking?')
%p.gl-text-center.gl-mb-0
- jira_docs_link_url = help_page_url('integration/jira/issues', anchor: 'view-jira-issues')
- jira_docs_link_url = help_page_url('integration/jira/configure', anchor: 'view-jira-issues')
- jira_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: jira_docs_link_url }
= html_escape(s_('JiraService|%{jira_docs_link_start}Enable the Jira integration%{jira_docs_link_end} to view your Jira issues in GitLab.')) % { jira_docs_link_start: jira_docs_link_start.html_safe, jira_docs_link_end: '</a>'.html_safe }
%p.gl-text-center.gl-mb-0.gl-text-gray-500

View File

@ -18,5 +18,5 @@
= _('- Not available to run jobs.')
%p
= s_("Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run.")
= s_("Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure runners only handle the jobs they are equipped to run.")
= link_to _("Learn more."), help_page_path("ci/runners/configure_runners", anchor: "how-the-runner-uses-tags"), target: '_blank'

View File

@ -12,5 +12,5 @@
title: s_('Runners|This runner is associated with specific projects.'),
dismissible: false) do |c|
- c.with_body do
= s_('Runners|You can set up a project runner to be used by multiple projects but you cannot make this a shared or group runner.')
= s_('Runners|You can set up a project runner to be used by multiple projects but you cannot make this an instance or group runner.')
= link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'project-runners'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -1,7 +1,7 @@
- shared_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('ci/runners/runners_scope', anchor: 'shared-runners') }
%h4
= _('Shared runners')
= s_('Runners|Instance runners')
.bs-callout{ data: { testid: 'shared-runners-description' } }
%p= s_('Runners|%{link_start}These runners%{link_end} are available to all groups and projects.').html_safe % { link_start: shared_link_start, link_end: '</a>'.html_safe }

View File

@ -21,7 +21,7 @@ module LimitedCapacity
def register(jid, max_jids)
with_redis do |redis|
redis.eval(LUA_REGISTER_SCRIPT, keys: [counter_key], argv: [jid.to_s, max_jids.to_i])
redis.eval(LUA_REGISTER_SCRIPT, keys: [counter_key], argv: [jid, max_jids])
end.present?
end
@ -59,7 +59,7 @@ module LimitedCapacity
end
def remove_job_keys(redis, keys)
redis.srem?(counter_key, keys) if keys.present?
redis.srem?(counter_key, keys)
end
def with_redis(&block)

View File

@ -21,6 +21,12 @@ end
# :nocov:
# rubocop:enable Gitlab/NoCodeCoverageComment
Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor)
Redis::Cluster::NodeLoader.prepend(Gitlab::Patch::NodeLoader)
Redis::Cluster::SlotLoader.prepend(Gitlab::Patch::SlotLoader)
Redis::Cluster::CommandLoader.prepend(Gitlab::Patch::CommandLoader)
Redis::Cluster.prepend(Gitlab::Patch::RedisCluster)
# this only instruments `RedisClient` used in `Sidekiq.redis`
RedisClient.register(Gitlab::Instrumentation::RedisClientMiddleware)
RedisClient.prepend(Gitlab::Patch::RedisClient)

View File

@ -25,17 +25,7 @@ raise "Do not configure cable.yml with a Redis Cluster as ActionCable only works
# https://github.com/rails/rails/blob/bb5ac1623e8de08c1b7b62b1368758f0d3bb6379/actioncable/lib/action_cable/subscription_adapter/redis.rb#L18
ActionCable::SubscriptionAdapter::Redis.redis_connector = lambda do |config|
args = config.except(:adapter, :channel_prefix)
.merge(custom: { instrumentation_class: "ActionCable" })
if args[:url]
# cable.yml uses the url key but unix sockets needs to be passed into Redis
# under the `path` key. We do a simple reassignment to resolve that.
uri = URI.parse(args[:url])
if uri.scheme == 'unix'
args[:path] = uri.path
args.delete(:url)
end
end
.merge(instrumentation_class: ::Gitlab::Instrumentation::Redis::ActionCable)
::Redis.new(args)
end

View File

@ -6,7 +6,7 @@ Peek::Adapters::Redis.prepend ::Gitlab::PerformanceBar::RedisAdapterWhenPeekEnab
Peek.singleton_class.prepend ::Gitlab::PerformanceBar::WithTopLevelWarnings
Rails.application.config.peek.adapter = :redis, {
client: Gitlab::Redis::Cache.redis,
client: ::Redis.new(Gitlab::Redis::Cache.params),
expires_in: 5.minutes
}

View File

@ -29,12 +29,12 @@ cookie_key = if Rails.env.development?
"_gitlab_session"
end
::Redis::Store::Factory.prepend(Gitlab::Patch::RedisStoreFactory)
store = Gitlab::Redis::Sessions.store(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE)
Rails.application.configure do
config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
redis_server: Gitlab::Redis::Sessions.params.merge(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE),
redis_store: store,
key: cookie_key,
secure: Gitlab.config.gitlab.https,
httponly: true,

View File

@ -28,7 +28,7 @@ def enable_semi_reliable_fetch_mode?
end
# Custom Queues configuration
queues_config_hash = Gitlab::Redis::Queues.params
queues_config_hash = Gitlab::Redis::Queues.redis_client_params
enable_json_logs = Gitlab.config.sidekiq.log_format != 'text'

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddCascadeMathRenderingLimits < Gitlab::Database::Migration[2.2]
milestone '16.9'
enable_lock_retries!
def change
add_column :namespace_settings, :math_rendering_limits_enabled, :boolean, null: true
add_column :namespace_settings, :lock_math_rendering_limits_enabled, :boolean, default: false, null: false
add_column :application_settings, :lock_math_rendering_limits_enabled, :boolean, default: false, null: false
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DeleteProjectIdComponentIdIndex < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '16.9'
INDEX_NAME = 'index_sbom_occurrences_on_project_id_component_id'
def up
remove_concurrent_index_by_name :sbom_occurrences, INDEX_NAME
end
def down
add_concurrent_index :sbom_occurrences, [:project_id, :component_id], name: INDEX_NAME
end
end

View File

@ -0,0 +1 @@
1c76bf19f1eef758de90d3a9750c28e40e8070d9efcd12d63552301a42201e92

View File

@ -0,0 +1 @@
bafc4d42dfa19a97208d9967f444d65c5069d44accd48ebf54faf187f981280c

View File

@ -12650,6 +12650,7 @@ CREATE TABLE application_settings (
rate_limits jsonb DEFAULT '{}'::jsonb NOT NULL,
elasticsearch_max_code_indexing_concurrency integer DEFAULT 30 NOT NULL,
enable_member_promotion_management boolean DEFAULT false NOT NULL,
lock_math_rendering_limits_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
@ -20113,6 +20114,8 @@ CREATE TABLE namespace_settings (
lock_toggle_security_policy_custom_ci boolean DEFAULT false NOT NULL,
toggle_security_policies_policy_scope boolean,
lock_toggle_security_policies_policy_scope boolean DEFAULT false NOT NULL,
math_rendering_limits_enabled boolean,
lock_math_rendering_limits_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))
@ -35494,8 +35497,6 @@ CREATE INDEX index_sbom_occurrences_on_project_id_and_id ON sbom_occurrences USI
CREATE INDEX index_sbom_occurrences_on_project_id_and_package_manager ON sbom_occurrences USING btree (project_id, package_manager);
CREATE INDEX index_sbom_occurrences_on_project_id_component_id ON sbom_occurrences USING btree (project_id, component_id);
CREATE INDEX index_sbom_occurrences_on_source_id ON sbom_occurrences USING btree (source_id);
CREATE UNIQUE INDEX index_sbom_occurrences_on_uuid ON sbom_occurrences USING btree (uuid);

View File

@ -501,7 +501,8 @@ To sync all groups manually when debugging is unnecessary,
[use the Rake task](../../raketasks/ldap.md#run-a-group-sync) instead.
The output from a manual [group sync](ldap_synchronization.md#group-sync) can show you what happens
when GitLab syncs its LDAP group memberships against LDAP.
when GitLab syncs its LDAP group memberships against LDAP. Enter the [rails console](#rails-console)
and then run:
```ruby
Rails.logger.level = Logger::DEBUG

View File

@ -124,7 +124,7 @@ To make sure your configuration is correct:
1. Run in the console:
```ruby
redis = Gitlab::Redis::SharedState.redis
redis = Redis.new(Gitlab::Redis::SharedState.params)
redis.info
```

View File

@ -4548,7 +4548,9 @@ Input type: `GroupUpdateInput`
| ---- | ---- | ----------- |
| <a id="mutationgroupupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationgroupupdatefullpath"></a>`fullPath` | [`ID!`](#id) | Full path of the group that will be updated. |
| <a id="mutationgroupupdatesharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting!`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
| <a id="mutationgroupupdatelockmathrenderinglimitsenabled"></a>`lockMathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are locked for all descendant groups. |
| <a id="mutationgroupupdatemathrenderinglimitsenabled"></a>`mathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are used for this group. |
| <a id="mutationgroupupdatesharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
#### Fields
@ -19587,6 +19589,8 @@ GPG signature for a signed commit.
| <a id="groupid"></a>`id` | [`ID!`](#id) | ID of the namespace. |
| <a id="groupistemporarystorageincreaseenabled"></a>`isTemporaryStorageIncreaseEnabled` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 16.7. Feature removal, will be completely removed in 17.0. |
| <a id="grouplfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
| <a id="grouplockmathrenderinglimitsenabled"></a>`lockMathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are locked for all descendant groups. |
| <a id="groupmathrenderinglimitsenabled"></a>`mathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are used for this group. |
| <a id="groupmaxaccesslevel"></a>`maxAccessLevel` | [`AccessLevel!`](#accesslevel) | The maximum access level of the current user in the group. |
| <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. |
| <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. |

View File

@ -719,7 +719,9 @@ Example response:
]
}
],
"ip_restriction_ranges": null
"ip_restriction_ranges": null,
"math_rendering_limits_enabled": true,
"lock_math_rendering_limits_enabled": false
}
```
@ -1025,18 +1027,20 @@ PUT /groups/:id
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `extra_shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Additional compute minutes for this group. Self-managed, Premium and Ultimate only. |
| `file_template_project_id` | integer | no | The ID of a project to load custom file templates from. Premium and Ultimate only. |
| `membership_lock` | boolean | no | Users cannot be added to projects in this group. Premium and Ultimate only. |
| `prevent_forking_outside_group` | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. Premium and Ultimate only. |
| `shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. Self-managed, Premium and Ultimate only. |
| `unique_project_download_limit` | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. Ultimate only. |
| `unique_project_download_limit_interval_in_seconds` | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). Ultimate only. |
| `unique_project_download_limit_allowlist` | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. Ultimate only.|
| `unique_project_download_limit_alertlist` | array of integers | no | List of user IDs that are emailed when the unique project download limit is exceeded. Available only on top-level groups. Default: `[]`, Maximum: 100 user IDs. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110201) in GitLab 15.9. Ultimate only.|
| `auto_ban_user_on_excessive_projects_download` | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. Ultimate only.|
| `ip_restriction_ranges` | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. Premium and Ultimate only.|
| `wiki_access_level` | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. Premium and Ultimate only.|
| `extra_shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Additional compute minutes for this group. Self-managed, Premium and Ultimate only. |
| `file_template_project_id` | integer | no | The ID of a project to load custom file templates from. Premium and Ultimate only. |
| `membership_lock` | boolean | no | Users cannot be added to projects in this group. Premium and Ultimate only. |
| `prevent_forking_outside_group` | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. Premium and Ultimate only. |
| `shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. Self-managed, Premium and Ultimate only. |
| `unique_project_download_limit` | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. Ultimate only. |
| `unique_project_download_limit_interval_in_seconds` | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). Ultimate only. |
| `unique_project_download_limit_allowlist` | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. Ultimate only.|
| `unique_project_download_limit_alertlist` | array of integers | no | List of user IDs that are emailed when the unique project download limit is exceeded. Available only on top-level groups. Default: `[]`, Maximum: 100 user IDs. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110201) in GitLab 15.9. Ultimate only.|
| `auto_ban_user_on_excessive_projects_download` | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. Ultimate only.|
| `ip_restriction_ranges` | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. Premium and Ultimate only.|
| `wiki_access_level` | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. Premium and Ultimate only.|
| `math_rendering_limits_enabled` | boolean | no | Indicates if math rendering limits are used for this group.|
| `lock_math_rendering_limits_enabled` | boolean | no | Indicates if math rendering limits are locked for all descendent groups.|
NOTE:
The `projects` and `shared_projects` attributes in the response are deprecated and [scheduled for removal in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
@ -1115,7 +1119,9 @@ Example response:
"request_access_enabled": false
}
],
"ip_restriction_ranges": null
"ip_restriction_ranges": null,
"math_rendering_limits_enabled": true,
"lock_math_rendering_limits_enabled": false
}
```

View File

@ -2494,7 +2494,7 @@ JWTs created this way support OIDC authentication. The required `aud` sub-keywor
job_with_id_tokens:
id_tokens:
ID_TOKEN_1:
aud: https://gitlab.com
aud: https://vault.example.com
ID_TOKEN_2:
aud:
- https://gcp.com
@ -2502,8 +2502,9 @@ job_with_id_tokens:
SIGSTORE_ID_TOKEN:
aud: sigstore
script:
- command_to_authenticate_with_gitlab $ID_TOKEN_1
- command_to_authenticate_with_vault $ID_TOKEN_1
- command_to_authenticate_with_aws $ID_TOKEN_2
- command_to_authenticate_with_gcp $ID_TOKEN_2
```
**Related topics**:

View File

@ -216,7 +216,7 @@ GitLab Duo Chat is enabled in the [Staging](https://staging.gitlab.com) and
[Staging Ref](https://staging-ref.gitlab.com/) GitLab environments.
Because GitLab Duo Chat is currently only available to members of groups in the
Premium tier, Staging Ref may be an easier place to test changes as a GitLab
Premium and Ultimate tiers, Staging Ref may be an easier place to test changes as a GitLab
team member because
[you can make yourself an instance Admin in Staging Ref](https://handbook.gitlab.com/handbook/engineering/infrastructure/environments/staging-ref/#admin-access)
and, as an Admin, easily create licensed groups for testing.

View File

@ -27,9 +27,15 @@ in the relevant projects:
We also run some documentation tests in the:
- GitLab CLI project: <https://gitlab.com/gitlab-org/cli/-/blob/main/.gitlab-ci.yml>
- GitLab Development Kit project:
<https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/.gitlab/ci/test.gitlab-ci.yml>.
- Gitaly project: <https://gitlab.com/gitlab-org/gitaly/-/blob/master/.gitlab-ci.yml>.
- GitLab Duo Plugin for JetBrains: <https://gitlab.com/gitlab-org/editor-extensions/gitlab-jetbrains-plugin/-/blob/main/.gitlab-ci.yml>
- GitLab VS Code Extension project: <https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/.gitlab-ci.yml>.
- GitLab Plugin for Neovim project: <https://gitlab.com/gitlab-org/editor-extensions/gitlab.vim/-/blob/main/.gitlab-ci.yml>.
- GitLab Language Server project: <https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/blob/main/.gitlab-ci.yml>.
- GitLab Extension for Visual Studio project: <https://gitlab.com/gitlab-org/editor-extensions/gitlab-visual-studio-extension/-/blob/main/.gitlab-ci.yml>.
## Run tests locally

View File

@ -60,18 +60,10 @@ On rows where `json.event` is `Failed Attempt`, you can find valuable debugging
#### View Telesign SMS status update logs
To view logs of Telesign status updates for an SMS sent to a user:
1. Get a `telesign_reference_id` value for an SMS sent to a specific user:
To view Telesign status updates logs for SMS sent to a user, query the GitLab production logs with:
```plaintext
json.message: "IdentityVerification::Phone" AND json.username:<username>`
```
1. Search for status update logs associated with `telesign_reference_id` value:
```plaintext
json.message: "IdentityVerification::Phone" AND json.event: "Telesign transaction status update" AND json.telesign_reference_id:replace_ref_id_value_here`
json.message: "IdentityVerification::Phone" AND json.event: "Telesign transaction status update" AND json.username:<username>`
```
Status update logs include the following fields:

View File

@ -81,10 +81,10 @@ Developers are highly encouraged to use [hash-tags](https://redis.io/docs/refere
where appropriate to facilitate future adoption of Redis Cluster in more Redis types. For example, the Namespace model uses hash-tags
for its [config cache keys](https://gitlab.com/gitlab-org/gitlab/-/blob/1a12337058f260d38405886d82da5e8bb5d8da0b/app/models/namespace.rb#L786).
To perform multi-key commands, developers may use the [`.pipelined`](https://github.com/redis-rb/redis-cluster-client#interfaces) method which splits and sends commands to each node and aggregates replies.
To perform multi-key commands, developers may use the [`Gitlab::Redis::CrossSlot::Pipeline`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/redis/cross_slot.rb) wrapper.
However, this does not work for [transactions](https://redis.io/docs/interact/transactions/) as Redis Cluster does not support cross-slot transactions.
For `Rails.cache`, we handle the `MGET` command found in `read_multi_get` by [patching it](https://gitlab.com/gitlab-org/gitlab/-/blob/c2bad2aac25e2f2778897bd4759506a72b118b15/lib/gitlab/patch/redis_cache_store.rb#L10) to use the `.pipelined` method.
For `Rails.cache`, we handle the `MGET` command found in `read_multi_get` by [patching it](https://gitlab.com/gitlab-org/gitlab/-/blob/c2bad2aac25e2f2778897bd4759506a72b118b15/lib/gitlab/patch/redis_cache_store.rb#L10) to use the `Gitlab::Redis::CrossSlot::Pipeline` wrapper.
The minimum size of the pipeline is set to 1000 commands and it can be adjusted by using the `GITLAB_REDIS_CLUSTER_PIPELINE_BATCH_LIMIT` environment variable.
## Redis in structured logging

View File

@ -147,8 +147,8 @@ module Gitlab
# Don't use multistore if redis.foo configuration is not provided
return super if config_fallback?
primary_store = init_redis(params)
secondary_store = init_redis(config_fallback.params)
primary_store = ::Redis.new(params)
secondary_store = ::Redis.new(config_fallback.params)
MultiStore.new(primary_store, secondary_store, store_name)
end

View File

@ -23,7 +23,7 @@ Prerequisites:
- You must have a [Jira Cloud API token](#create-a-jira-cloud-api-token) and the email address you used to create the token.
- If you've enabled
[IP allowlists](https://support.atlassian.com/security-and-access-policies/docs/specify-ip-addresses-for-product-access/), add the
[GitLab.com IP range](../../user/gitlab_com/index.md#ip-range) to the allowlist to [view Jira issues](issues.md#view-jira-issues) in GitLab.
[GitLab.com IP range](../../user/gitlab_com/index.md#ip-range) to the allowlist to [view Jira issues](#view-jira-issues) in GitLab.
- **For Jira Data Center or Jira Server**, you must have one of the following:
- [Jira username and password](jira_server_configuration.md).
- Jira personal access token (GitLab 16.0 and later).
@ -67,7 +67,7 @@ To configure your project settings in GitLab:
- For **Jira issue regex**, [enter a regex pattern](issues.md#define-a-regex-pattern).
- For **Jira issue prefix**, [enter a prefix](issues.md#define-a-prefix).
1. Optional. In the **Issues** section:
- To [view issues](issues.md#view-jira-issues) from a single Jira project in a GitLab project:
- To [view issues](#view-jira-issues) from a single Jira project in a GitLab project:
1. Select the **Enable Jira issues** checkbox.
1. Enter the Jira project key.
@ -86,6 +86,36 @@ To configure your project settings in GitLab:
Your GitLab project can now interact with all Jira projects on your instance.
## View Jira issues
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** SaaS, self-managed
> - Ability to enable Jira issues at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325715) in GitLab 16.9.
Prerequisites:
- Ensure the Jira issue integration is [configured](#configure-the-integration)
and the **Enable Jira issues** checkbox is selected.
To view issues from a single Jira project in a GitLab project:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Plan > Jira issues**.
By default, the issues are sorted by **Created date**.
The most recently created issues appear at the top.
You can [search and filter the issue list](issues.md#search-and-filter-the-issue-list)
and select an issue to view that issue in GitLab.
Issues are grouped into the following tabs based on their
[Jira status](https://confluence.atlassian.com/adminjiraserver070/defining-status-field-values-749382903.html):
- **Open**: issues with any Jira status other than **Done**.
- **Closed**: issues with a **Done** Jira status.
- **All**: issues with any Jira status.
## Create a Jira issue for a vulnerability
DETAILS:

View File

@ -48,7 +48,7 @@ This table shows the features available with the Jira issue integration and the
| Mention a Jira issue ID in a GitLab branch name, and the Jira issue shows the branch name. | **{dotted-circle}** No | **{check-circle}** Yes, in the Jira issue's development panel. |
| Add time tracking to a Jira issue. | **{dotted-circle}** No | **{check-circle}** Yes, with Jira Smart Commits. |
| Use a GitLab commit or merge request to transition a Jira issue. |**{check-circle}** Yes, only a single transition. Typically used to close the Jira issue. | **{check-circle}** Yes, transition the Jira issue to any state with Jira Smart Commits. |
| [View a list of Jira issues](issues.md#view-jira-issues). | **{check-circle}** Yes | **{dotted-circle}** No |
| [View a list of Jira issues](configure.md#view-jira-issues). | **{check-circle}** Yes | **{dotted-circle}** No |
| [Create a Jira issue for a vulnerability](configure.md#create-a-jira-issue-for-a-vulnerability). | **{check-circle}** Yes | **{dotted-circle}** No |
| Create a GitLab branch from a Jira issue. | **{dotted-circle}** No | **{check-circle}** Yes, in the Jira issue's development panel. |
| Mention a Jira issue ID in a GitLab merge request, branch name, or any of the last 5,000 commits to the branch after the last successful deployment to the environment to sync a GitLab deployment to a Jira issue. | **{dotted-circle}** No | **{check-circle}** Yes, in the Jira issue's development panel. |

View File

@ -146,34 +146,7 @@ Consider this example:
- GitLab adds a formatted comment to Jira, linking back to the commit that
resolved the issue. You can [disable comments](#disable-comments-on-jira-issues).
## View Jira issues
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** SaaS, self-managed
You can view and search issues from a selected Jira project directly in GitLab,
provided your GitLab administrator [has configured the integration](configure.md#configure-the-integration).
To view Jira issues:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Plan > Jira issues**.
The issues are sorted by **Created date** by default, with the most recently created issues listed at the top.
- To display the most recently updated issues first, select **Updated date**.
- You can [search and filter the issue list](#search-and-filter-the-issue-list).
- You can [select an issue from the list to view the issue in GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/299832).
Issues are grouped into tabs based on their
[Jira status](https://confluence.atlassian.com/adminjiraserver070/defining-status-field-values-749382903.html):
- **Open** tab: All issues with a Jira status in any category other than Done.
- **Closed** tab: All issues with a Jira status categorized as Done.
- **All** tab: All issues of any status.
### Search and filter the issue list
## Search and filter the issue list
DETAILS:
**Tier:** Premium, Ultimate

View File

@ -217,7 +217,7 @@ end
## Jira issue list
When [viewing Jira issues](issues.md#view-jira-issues) in GitLab, you might encounter the following issues.
When [viewing Jira issues](configure.md#view-jira-issues) in GitLab, you might encounter the following issues.
### `500 We're sorry` when accessing a Jira issue in GitLab

View File

@ -19,7 +19,7 @@ Some features are still in development. View details about [support for each sta
| Goal | Feature | Tier/Offering/Status |
|---|---|---|
| Helps you write code more efficiently by showing code suggestions as you type. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=hCAyCTacdAQ) | [Code Suggestions](project/repository/code_suggestions/index.md) | **Free Open Access** subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/) available through February 14, 2024:<br>- SaaS, All subscription tiers <br>- Self-managed, Premium and Ultimate tiers<br><br> **Generally Available** starting February 15, 2024: <br> - SaaS, self-managed <br>- Premium and Ultimate tiers as part of [GitLab Duo Pro](https://about.gitlab.com/press/releases/2024-01-17-gitlab-announces-pricing-of-gitLab-duo-pro.html/) |
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Chat](gitlab_duo_chat.md) | **Beta Access** subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/):<br>- SaaS, self-managed <br>- Ultimate tier<br><br>**Status:** Beta |
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Chat](gitlab_duo_chat.md) | **Beta Access** subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/):<br>- SaaS, self-managed <br>- Premium and Ultimate tiers<br><br>**Status:** Beta |
| Helps you discover or recall Git commands when and where you need them. | [Git suggestions](../editor_extensions/gitlab_cli/index.md#gitlab-duo-commands) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
| Generates issue descriptions. | [Issue description generation](#summarize-an-issue-with-issue-description-generation) | **Tier:** Ultimate<br>**Offering:** SaaS <br>**Status:** Experiment |

View File

@ -182,7 +182,7 @@ To track DORA metrics in these cases, you can [create a deployment record](../..
### Measure DORA metrics with Jira
- Deployment frequency and lead time for changes are calculated based on GitLab CI/CD and Merge Requests (MRs), and do not require Jira data.
- Time to restore service and change failure rate require [GitLab incidents](../../operations/incident_management/manage_incidents.md) for the calculation. For more information, see [Measure DORA Time to restore service and Change failure rate with external incidents](#measure-dora-time-to-restore-service-and-change-failure-rate-with-external-incidents).
- Time to restore service and change failure rate require [GitLab incidents](../../operations/incident_management/manage_incidents.md) for the calculation. For more information, see [Measure DORA Time to restore service and Change failure rate with external incidents](#measure-dora-time-to-restore-service-and-change-failure-rate-with-external-incidents) and the [Jira incident replicator guide](https://gitlab.com/smathur/jira-incident-replicator).
### Measure DORA Time to restore service and Change failure rate with external incidents

View File

@ -7,13 +7,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab Duo Chat
DETAILS:
**Tier:** Ultimate
**Tier:** Premium, Ultimate
**Offering:** SaaS, self-managed
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117695) as an [Experiment](../policy/experiment-beta-support.md#experiment) for SaaS in GitLab 16.0.
> - Changed to [Beta](../policy/experiment-beta-support.md#beta) for SaaS in GitLab 16.6.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/11251) as a [Beta](../policy/experiment-beta-support.md#beta) for self-managed in GitLab 16.8.
> - Changed from Ultimate to [Premium](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142808) tier in GitLab 16.9.
GitLab Duo Chat is your personal AI-powered assistant for boosting productivity.
It can assist various tasks of your daily work with the AI-generated content.

View File

@ -102,14 +102,10 @@ and tags. For example, if the template contains a protected branch:
- In the project created from the template, the branch allows _you_ to merge into
the default branch.
<!-- ## Troubleshooting
## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
### Administrator cannot see custom group-level project templates when creating a project
Each scenario can be a third-level heading, for example `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
Custom group-level project templates are only available to group members.
If the administrator account you are using is not a member of a group,
you can't access the templates.

View File

@ -450,7 +450,7 @@ for the ability to set merge request approval rules for groups is tracked in
## Enable Experiment and Beta features
DETAILS:
**Tier:** Ultimate, Premium
**Tier:** Premium, Ultimate
**Offering:** SaaS
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118222) in GitLab 16.0.

View File

@ -71,7 +71,10 @@ To view the organizations you have access to:
1. On the left sidebar, select **Organizations** (**{organization}**) and find the organization you want to manage.
1. Select **Manage > Groups and projects**.
1. To switch between groups and projects, use the **Display** filter next to the search box.
1. Optional. Filter the results:
- To search for specific groups or projects, in the search box enter your search term.
- To view only groups or projects, from the **Display** dropdown list select an option.
1. Optional. To sort the results by name, date created, or date updated, from the dropdown list select an option. Then select ascending (**{sort-lowest}**) or descending (**{sort-highest}**) order.
## Manage users

View File

@ -20,7 +20,7 @@ Watch a [video demo](https://youtu.be/yvLxtkvsFDA) of how to publish npm package
### Authentication to the package registry
You need an token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
You need a token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
- If your organization uses two factor authentication (2FA), you must use a personal access token with the scope set to `api`.
- If you are publishing a package via CI/CD pipelines, you must use a CI job token.

View File

@ -182,9 +182,9 @@ For more information, [see the forking workflow documentation](../repository/for
### Set the default target project
By default, merge requests originating from a fork target your fork, not the upstream project.
If you frequently contribute back to the upstream project, and want to target it
by default, change the default target for your fork.
By default, merge requests originating from a fork target the upstream project, not the forked project.
You can configure your forked project to be the default target rather than the upstream project.
Prerequisites:

View File

@ -14,6 +14,8 @@ module API
expose :emails_enabled, documentation: { type: 'boolean' }
expose :mentions_disabled
expose :lfs_enabled?, as: :lfs_enabled
expose :math_rendering_limits_enabled, documentation: { type: 'boolean' }
expose :lock_math_rendering_limits_enabled, documentation: { type: 'boolean' }
expose :default_branch_protection
expose :default_branch_protection_defaults
expose :avatar_url do |group, options|

View File

@ -43,6 +43,8 @@ module API
params :optional_update_params do
optional :prevent_sharing_groups_outside_hierarchy, type: Boolean, desc: 'Prevent sharing groups within this namespace with any groups outside the namespace. Only available on top-level groups.'
optional :lock_math_rendering_limits_enabled, type: Boolean, desc: 'Indicates if math rendering limits are locked for all descendent groups.'
optional :math_rendering_limits_enabled, type: Boolean, desc: 'Indicates if math rendering limits are used for this group.'
end
params :optional_update_params_ee do

View File

@ -5,6 +5,7 @@ module Banzai
class BroadcastMessagePipeline < DescriptionPipeline
def self.filters
@filters ||= FilterArray[
Filter::BlockquoteFenceFilter,
Filter::MarkdownFilter,
Filter::BroadcastMessageSanitizationFilter,
Filter::EmojiFilter,

View File

@ -12,6 +12,7 @@ module Banzai
FilterArray[
Filter::MarkdownPreEscapeFilter,
Filter::DollarMathPreFilter,
Filter::BlockquoteFenceFilter,
Filter::MarkdownFilter,
Filter::DollarMathPostFilter,
Filter::MarkdownPostEscapeFilter

View File

@ -8,7 +8,6 @@ module Banzai
Filter::NormalizeSourceFilter,
Filter::TruncateSourceFilter,
Filter::FrontMatterFilter,
Filter::BlockquoteFenceFilter,
]
end

View File

@ -31,7 +31,7 @@ module Gitlab
Gitlab::Redis::SharedState.with do |redis|
redis.multi do |r|
r.zadd(key, time, ip) if ip
r.zadd(key, time, ip)
r.zremrangebyscore(key, 0, time - config.unique_ips_limit_time_window)
r.zcard(key)
end.last

View File

@ -14,7 +14,7 @@ module Gitlab
ttl_jitter = 2.hours.to_i
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
keys.each { |key| pipeline.expire(key, ttl_duration + rand(-ttl_jitter..ttl_jitter)) }
end
end
@ -25,7 +25,7 @@ module Gitlab
end
def redis
@redis ||= Gitlab::Redis::Cache.redis
@redis ||= ::Redis.new(Gitlab::Redis::Cache.params)
end
end
end

View File

@ -99,7 +99,7 @@ module Gitlab
def store_in_cache
with_redis do |redis|
redis.pipelined do |p|
p.mapped_hmset(cache_key, { sha: sha.to_s, status: status.to_s, ref: ref.to_s })
p.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref })
p.expire(cache_key, STATUS_KEY_TTL)
end
end

View File

@ -139,7 +139,7 @@ module Gitlab
key = cache_key_for(raw_key)
with_redis do |redis|
redis.sismember(key, value || value.to_s)
redis.sismember(key, value)
end
end
@ -162,7 +162,7 @@ module Gitlab
def self.write_multiple(mapping, key_prefix: nil, timeout: TIMEOUT)
with_redis do |redis|
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
mapping.each do |raw_key, value|
key = cache_key_for("#{key_prefix}#{raw_key}")

View File

@ -197,9 +197,7 @@ module Gitlab
record_hit_ratio(results)
results.map! do |result|
unless result.nil?
Gitlab::Json.parse(gzip_decompress(result.force_encoding(Encoding::UTF_8)), symbolize_names: true)
end
Gitlab::Json.parse(gzip_decompress(result), symbolize_names: true) unless result.nil?
end
file_paths.zip(results).to_h

View File

@ -16,7 +16,7 @@ module Gitlab
def write_multiple(mapping)
with_redis do |redis|
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.pipelined do |pipelined|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipelined|
mapping.each do |raw_key, value|
key = cache_key_for(raw_key)
@ -42,7 +42,7 @@ module Gitlab
with_redis do |redis|
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
if Gitlab::Redis::ClusterUtil.cluster?(redis)
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
keys.each { |key| pipeline.get(key) }
end
else
@ -54,7 +54,7 @@ module Gitlab
content.map! do |lines|
next unless lines
Gitlab::Json.parse(gzip_decompress(lines.force_encoding(Encoding::UTF_8))).map! do |line|
Gitlab::Json.parse(gzip_decompress(lines)).map! do |line|
Gitlab::Diff::Line.safe_init_from_hash(line)
end
end

View File

@ -17,7 +17,7 @@ module Gitlab
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
with_redis do |redis|
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
keys.each_with_index do |key, i|
pipeline.set(redis_shared_state_key(key), etags[i], ex: EXPIRY_TIME, nx: only_if_missing)
end

View File

@ -60,7 +60,6 @@ module Gitlab
def self.cancel(key, uuid)
return unless key.present?
return unless uuid.present?
Gitlab::Redis::SharedState.with do |redis|
redis.eval(LUA_CANCEL_SCRIPT, keys: [ensure_prefixed_key(key)], argv: [uuid])
@ -111,7 +110,7 @@ module Gitlab
# false if the lease is taken by a different UUID or inexistent.
def renew
Gitlab::Redis::SharedState.with do |redis|
result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_shared_state_key], argv: [@uuid, @timeout.to_i])
result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_shared_state_key], argv: [@uuid, @timeout])
result == @uuid
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rails'
require 'redis-clustering'
require 'redis'
module Gitlab
module Instrumentation
@ -230,7 +230,7 @@ module Gitlab
end
def key_slot(key)
::RedisClient::Cluster::KeySlotConverter.convert(extract_hash_tag(key))
::Redis::Cluster::KeySlotConverter.convert(extract_hash_tag(key))
end
# This is almost identical to Redis::Cluster::Command#extract_hash_tag,

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
module Gitlab
module Instrumentation
module RedisInterceptor
include RedisHelper
def call(command)
instrument_call([command], instrumentation_class) do
super
end
end
def call_pipeline(pipeline)
instrument_call(pipeline.commands, instrumentation_class, true) do
super
end
end
def write(command)
measure_write_size(command, instrumentation_class) if ::RequestStore.active?
super
end
def read
result = super
measure_read_size(result, instrumentation_class) if ::RequestStore.active?
result
end
def ensure_connected
super do
instrument_reconnection_errors do
yield
end
end
end
def instrument_reconnection_errors
yield
rescue ::Redis::BaseConnectionError => ex
instrumentation_class.instance_count_connection_exception(ex)
raise ex
end
# That's required so it knows which GitLab Redis instance
# it's interacting with in order to categorize accordingly.
#
def instrumentation_class
@options[:instrumentation_class] # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
end
end

View File

@ -100,7 +100,7 @@ module Gitlab
def refresh_keys_expiration
with_redis do |redis|
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
pipeline.expire(issue_ids_key, REDIS_EXPIRY_TIME)
pipeline.expire(current_index_key, REDIS_EXPIRY_TIME)
pipeline.expire(current_project_key, REDIS_EXPIRY_TIME)

View File

@ -11,7 +11,7 @@ module Gitlab
data = Gitlab::Redis::Cache.with do |r|
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
r.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(r).pipelined do |pipeline|
subjects.each do |subject|
new(subject).read(pipeline)
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
module Gitlab
module Patch
module CommandLoader
extend ActiveSupport::Concern
class_methods do
# Shuffle the node list to spread out initial connection creation amongst all nodes
#
# The input is a Redis::Cluster::Node instance which is an Enumerable.
# `super` receives an Array of Redis::Client instead of a Redis::Cluster::Node
def load(nodes)
super(nodes.to_a.shuffle)
end
end
end
end
end

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
# Patch to address https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/2212#note_1287996694
# It uses hostname instead of IP address if the former is present in `CLUSTER NODES` output.
if Gem::Version.new(Redis::VERSION) > Gem::Version.new('4.8.1')
raise 'New version of redis detected, please remove or update this patch'
end
module Gitlab
module Patch
module NodeLoader
extend ActiveSupport::Concern
class_methods do
# Shuffle the node list to spread out initial connection creation amongst all nodes
#
# The input is a Redis::Cluster::Node instance which is an Enumerable.
# `super` receives an Array of Redis::Client instead of a Redis::Cluster::Node
def load_flags(nodes)
super(nodes.to_a.shuffle)
end
end
def self.prepended(base)
base.class_eval do
# monkey-patches https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/cluster/node_loader.rb#L23
def self.fetch_node_info(node)
node.call(%i[cluster nodes]).split("\n").map(&:split).to_h do |arr|
[
extract_host_identifier(arr[1]),
(arr[2].split(',') & %w[master slave]).first # rubocop:disable Naming/InclusiveLanguage
]
end
end
# Since `CLUSTER SLOT` uses the preferred endpoint determined by
# the `cluster-preferred-endpoint-type` config value, we will prefer hostname over IP address.
# See https://redis.io/commands/cluster-nodes/ for details on the output format.
#
# @param [String] Address info matching fhe format: <ip:port@cport[,hostname[,auxiliary_field=value]*]>
def self.extract_host_identifier(node_address)
ip_chunk, hostname, _auxiliaries = node_address.split(',')
return ip_chunk.split('@').first if hostname.blank?
port = ip_chunk.split('@').first.split(':')[1]
"#{hostname}:#{port}"
end
end
end
end
end
end

View File

@ -20,7 +20,7 @@ module Gitlab
delete_count = 0
redis.with do |conn|
entries.each_slice(pipeline_batch_size) do |subset|
delete_count += conn.pipelined do |pipeline|
delete_count += Gitlab::Redis::CrossSlot::Pipeline.new(conn).pipelined do |pipeline|
subset.each { |entry| pipeline.del(entry) }
end.sum
end
@ -58,7 +58,7 @@ module Gitlab
def pipeline_mget(conn, keys)
keys.each_slice(pipeline_batch_size).flat_map do |subset|
conn.pipelined do |p|
Gitlab::Redis::CrossSlot::Pipeline.new(conn).pipelined do |p|
subset.each { |key| p.get(key) }
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
# Patch to expose `find_node_key` method for cross-slot pipelining
# In redis v5.0.x, cross-slot pipelining is implemented via redis-cluster-client.
# This patch should be removed since there is no need for it.
# Gitlab::Redis::CrossSlot and its usage should be removed as well.
if Gem::Version.new(Redis::VERSION) != Gem::Version.new('4.8.0')
raise 'New version of redis detected, please remove or update this patch'
end
module Gitlab
module Patch
module RedisCluster
# _find_node_key exposes a private function of the same name in Redis::Cluster.
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/cluster.rb#L282
def _find_node_key(command)
find_node_key(command)
end
end
end
end

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Patch
module RedisStoreFactory
def create
# rubocop:disable Gitlab/ModuleWithInstanceVariables -- patched code references @options in redis-store
opt = @options
# rubocop:enable Gitlab/ModuleWithInstanceVariables
return Gitlab::Redis::ClusterStore.new(opt) if opt[:nodes]
super
end
end
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
module Gitlab
module Patch
module SlotLoader
extend ActiveSupport::Concern
class_methods do
# Shuffle the node list to spread out initial connection creation amongst all nodes
#
# The input is a Redis::Cluster::Node instance which is an Enumerable.
# `super` receives an Array of Redis::Client instead of a Redis::Cluster::Node
def load(nodes)
super(nodes.to_a.shuffle)
end
end
end
end
end

View File

@ -1,81 +0,0 @@
# frozen_string_literal: true
require 'redis-clustering'
require 'redis/store/ttl'
require 'redis/store/interface'
require 'redis/store/namespace'
require 'redis/store/serialization'
module Gitlab
module Redis
class ClusterStore < ::Redis::Cluster
include ::Redis::Store::Interface
def initialize(options = {})
orig_options = options.dup
@serializer = orig_options.key?(:serializer) ? orig_options.delete(:serializer) : Marshal
unless orig_options[:marshalling].nil?
# `marshalling` only used here, might not be supported in `super`
@serializer = orig_options.delete(:marshalling) ? Marshal : nil
end
_remove_unsupported_options(options)
super(options)
_extend_marshalling
_extend_namespace orig_options
end
# copies ::Redis::Store::Ttl implementation in a redis-v5 compatible manner
def set(key, value, options = nil)
ttl = get_ttl(options)
if ttl
setex(key, ttl.to_i, value, raw: true)
else
super(key, value)
end
end
# copies ::Redis::Store::Ttl implementation in a redis-v5 compatible manner
def setnx(key, value, options = nil)
ttl = get_ttl(options)
if ttl
multi do |m|
m.setnx(key, value)
m.expire(key, ttl)
end
else
super(key, value)
end
end
private
def get_ttl(options)
# https://github.com/redis-store/redis-store/blob/v1.10.0/lib/redis/store/ttl.rb#L37
options[:expire_after] || options[:expires_in] || options[:expire_in] if options
end
def _remove_unsupported_options(options)
# Unsupported keywords should be removed to avoid errors
# https://github.com/redis-rb/redis-client/blob/v0.13.0/lib/redis_client/config.rb#L21
options.delete(:raw)
options.delete(:serializer)
options.delete(:marshalling)
options.delete(:namespace)
options.delete(:scheme)
end
def _extend_marshalling
extend ::Redis::Store::Serialization unless @serializer.nil?
end
def _extend_namespace(options)
@namespace = options[:namespace]
extend ::Redis::Store::Namespace
end
end
end
end

View File

@ -13,14 +13,14 @@ module Gitlab
if obj.is_a?(MultiStore)
cluster?(obj.primary_store) || cluster?(obj.secondary_store)
else
obj.is_a?(::Redis::Cluster)
obj.respond_to?(:_client) && obj._client.is_a?(::Redis::Cluster)
end
end
def batch_unlink(keys, redis)
expired_count = 0
keys.each_slice(1000) do |subset|
expired_count += redis.pipelined do |pipeline|
expired_count += Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
subset.each { |key| pipeline.unlink(key) }
end.sum
end
@ -30,7 +30,7 @@ module Gitlab
# Redis cluster alternative to mget
def batch_get(keys, redis)
keys.each_slice(1000).flat_map do |subset|
redis.pipelined do |pipeline|
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
subset.map { |key| pipeline.get(key) }
end
end

View File

@ -1,53 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Redis
module CommandBuilder
extend self
# Ref: https://github.com/redis-rb/redis-client/blob/v0.19.1/lib/redis_client/command_builder.rb
# we modify the command builder to convert nil to strings as this behaviour was present in
# https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/connection/command_helper.rb#L20
#
# Note that we only adopt the Ruby3.x-compatible logic in .generate.
# Symbol.method_defined?(:name) is true in Ruby 3
def generate(args, kwargs = nil)
command = args.flat_map do |element|
case element
when Hash
element.flatten
else
element
end
end
kwargs&.each do |key, value|
if value
if value == true
command << key.name
else
command << key.name << value
end
end
end
command.map! do |element|
case element
when String
element
when Symbol
element.name
when Integer, Float, NilClass
element.to_s
else
raise TypeError, "Unsupported command argument type: #{element.class}"
end
end
raise ArgumentError, "can't issue an empty redis command" if command.empty?
command
end
end
end
end

View File

@ -0,0 +1,141 @@
# frozen_string_literal: true
module Gitlab
module Redis
module CrossSlot
class Router
attr_reader :node_mapping, :futures, :node_sequence, :cmd_queue
delegate :respond_to_missing?, to: :@redis
# This map contains redis-rb methods which does not map directly
# to a standard Redis command. It is used transform unsupported commands to standard commands
# to find the node key for unsupported commands.
#
# Redis::Cluster::Command only contains details of commands which the Redis Server
# returns. Hence, commands like mapped_hmget and hscan_each internally will call the
# base command, hmget and hscan respectively.
#
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/cluster/command.rb
UNSUPPORTED_CMD_MAPPING = {
# Internally, redis-rb calls the supported Redis command and transforms the output.
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/commands/hashes.rb#L104
mapped_hmget: :hmget
}.freeze
# Initializes the CrossSlot::Router
# @param {::Redis}
def initialize(redis)
@redis = redis
@node_mapping = {}
@futures = {}
@node_sequence = []
@cmd_queue = []
end
# For now we intercept every redis.call and return a Gitlab-Future object.
# This method groups every commands to a node for fan-out. Commands are grouped using the first key.
#
# rubocop:disable Style/MissingRespondToMissing
def method_missing(cmd, *args, **kwargs, &blk)
# Note that we can re-map the command without affecting execution as it is
# solely for finding the node key. The original cmd will be executed.
node = @redis._client._find_node_key([UNSUPPORTED_CMD_MAPPING.fetch(cmd, cmd)] + args)
@node_mapping[node] ||= []
@futures[node] ||= []
@node_sequence << node
@node_mapping[node] << [cmd, args, kwargs || {}, blk]
f = Future.new
@futures[node] << f
@cmd_queue << [f, cmd, args, kwargs || {}, blk]
f
end
# rubocop:enable Style/MissingRespondToMissing
end
# Wraps over redis-rb's Future in
# https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/pipeline.rb#L244
class Future
def set(future, is_val = false)
@redis_future = future
@is_val = is_val
end
def value
return @redis_val if @is_val
@redis_future.value
end
end
# Pipeline allows cross-slot pipelined to be called. The fan-out logic is implemented in
# https://github.com/redis-rb/redis-cluster-client/blob/master/lib/redis_client/cluster/pipeline.rb
# which is available in redis-rb v5.0.
#
# This file can be deprecated after redis-rb v4.8.0 is upgraded to v5.0
class Pipeline
# Initializes the CrossSlot::Pipeline
# @param {::Redis}
def initialize(redis)
@redis = redis
end
# pipelined is used in place of ::Redis `.pipelined` when running in a cluster context
# where cross-slot operations may happen.
def pipelined(&block)
# Directly call .pipelined and defer the pipeline execution to MultiStore.
# MultiStore could wrap over 0, 1, or 2 Redis Cluster clients, handling it here
# will not work for 2 clients since the key-slot topology can differ.
if use_cross_slot_pipelining?
router = Router.new(@redis)
yield router
execute_commands(router)
else
# use redis-rb's pipelined method
@redis.pipelined(&block)
end
end
private
def use_cross_slot_pipelining?
!@redis.instance_of?(::Gitlab::Redis::MultiStore) && @redis._client.instance_of?(::Redis::Cluster)
end
def execute_commands(router)
router.node_mapping.each do |node_key, commands|
# TODO possibly use Threads to speed up but for now `n` is 3-5 which is small.
@redis.pipelined do |p|
commands.each_with_index do |command, idx|
future = router.futures[node_key][idx]
cmd, args, kwargs, blk = command
future.set(p.public_send(cmd, *args, **kwargs, &blk)) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
router.node_sequence.map do |node_key|
router.futures[node_key].shift.value
end
rescue ::Redis::CommandError => err
if err.message.start_with?('MOVED', 'ASK')
Gitlab::ErrorTracking.log_exception(err)
return execute_commands_sequentially(router)
end
raise
end
def execute_commands_sequentially(router)
router.cmd_queue.map do |command|
future, cmd, args, kwargs, blk = command
future.set(@redis.public_send(cmd, *args, **kwargs, &blk), true) # rubocop:disable GitlabSecurity/PublicSend
future.value
end
end
end
end
end
end

View File

@ -432,6 +432,16 @@ module Gitlab
# rubocop:disable GitlabSecurity/PublicSend
def send_command(redis_instance, command_name, *args, **kwargs, &block)
# Run wrapped pipeline for each instance individually so that the fan-out is distinct.
# If both primary and secondary are Redis Clusters, the slot-node distribution could
# be different.
#
# We ignore args and kwargs since `pipelined` does not accept arguments
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis.rb#L164
if command_name.to_s == 'pipelined' && redis_instance._client.instance_of?(::Redis::Cluster)
return Gitlab::Redis::CrossSlot::Pipeline.new(redis_instance).pipelined(&block)
end
if block
# Make sure that block is wrapped and executed only on the redis instance that is executing the block
redis_instance.send(command_name, *args, **kwargs) do |*params|
@ -452,7 +462,7 @@ module Gitlab
end
def redis_store?(pool)
pool.with { |c| c.instance_of?(Gitlab::Redis::MultiStore) || c.is_a?(::Redis) || c.is_a?(::Redis::Cluster) }
pool.with { |c| c.instance_of?(Gitlab::Redis::MultiStore) || c.is_a?(::Redis) }
end
def validate_stores!

Some files were not shown because too many files have changed in this diff Show More