summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-03 09:10:18 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-03 09:10:18 +0000
commite5f183140340a76754af3beabff0fcd74212a102 (patch)
treeda0dcb2b596037e9ddda171d6ec63393462bae0f
parent685084aaf40a35358bd7c5135b08dc7e0d3439a7 (diff)
downloadgitlab-ce-e5f183140340a76754af3beabff0fcd74212a102.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_manual_todo.yml10
-rw-r--r--app/assets/javascripts/cycle_analytics/components/path_navigation.vue12
-rw-r--r--app/helpers/gitlab_script_tag_helper.rb8
-rw-r--r--app/models/integrations/base_slash_commands.rb67
-rw-r--r--app/models/integrations/mattermost_slash_commands.rb59
-rw-r--r--app/models/integrations/slack_slash_commands.rb36
-rw-r--r--app/models/postgresql/replication_slot.rb2
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb57
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb34
-rw-r--r--app/models/project_services/slash_commands_service.rb65
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml13
-rw-r--r--config/feature_flags/development/cached_sidebar_open_epics_count.yml8
-rw-r--r--config/feature_flags/development/create_cloud_run_clusters.yml8
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md13
-rw-r--r--doc/topics/autodevops/customize.md21
-rw-r--r--doc/user/group/epics/manage_epics.md27
-rw-r--r--doc/user/permissions.md11
-rw-r--r--lib/api/helpers/services_helpers.rb4
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb10
-rw-r--r--lib/gitlab/integrations/sti_type.rb4
-rw-r--r--lib/gitlab/sidekiq_middleware.rb9
-rw-r--r--lib/sidebars/projects/menus/security_compliance_menu.rb22
-rw-r--r--spec/controllers/projects/mattermosts_controller_spec.rb4
-rw-r--r--spec/factories/integrations.rb6
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb4
-rw-r--r--spec/frontend/cycle_analytics/path_navigation_spec.js12
-rw-r--r--spec/helpers/gitlab_script_tag_helper_spec.rb7
-rw-r--r--spec/helpers/webpack_helper_spec.rb3
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb28
-rw-r--r--spec/lib/gitlab/sidekiq_middleware_spec.rb303
-rw-r--r--spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb36
-rw-r--r--spec/models/integrations/mattermost_slash_commands_spec.rb (renamed from spec/models/project_services/mattermost_slash_commands_service_spec.rb)4
-rw-r--r--spec/models/integrations/slack_slash_commands_spec.rb (renamed from spec/models/project_services/slack_slash_commands_service_spec.rb)4
-rw-r--r--spec/models/postgresql/replication_slot_spec.rb4
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb4
-rw-r--r--spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb (renamed from spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb)2
-rw-r--r--spec/views/projects/clusters/clusters/gcp/_form.html.haml_spec.rb12
-rw-r--r--workhorse/doc/operations/configuration.md4
-rw-r--r--workhorse/internal/config/config.go17
-rw-r--r--workhorse/internal/redis/redis.go64
-rw-r--r--workhorse/internal/redis/redis_test.go16
42 files changed, 509 insertions, 529 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 7de8f7ba3f6..5992affe96a 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -1149,6 +1149,7 @@ RSpec/AnyInstanceOf:
- 'spec/models/hooks/system_hook_spec.rb'
- 'spec/models/hooks/web_hook_spec.rb'
- 'spec/models/integrations/jira_spec.rb'
+ - 'spec/models/integrations/mattermost_slash_commands_spec.rb'
- 'spec/models/issue_spec.rb'
- 'spec/models/key_spec.rb'
- 'spec/models/member_spec.rb'
@@ -1156,7 +1157,6 @@ RSpec/AnyInstanceOf:
- 'spec/models/merge_request_spec.rb'
- 'spec/models/note_spec.rb'
- 'spec/models/project_import_state_spec.rb'
- - 'spec/models/project_services/mattermost_slash_commands_service_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/models/repository_spec.rb'
- 'spec/models/user_spec.rb'
@@ -1289,8 +1289,8 @@ RSpec/AnyInstanceOf:
- 'spec/support/shared_examples/features/snippets_shared_examples.rb'
- 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb'
- 'spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb'
- - 'spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb'
- 'spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb'
+ - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb'
- 'spec/support/shared_examples/models/mentionable_shared_examples.rb'
- 'spec/support/shared_examples/models/with_uploads_shared_examples.rb'
- 'spec/support/shared_examples/path_extraction_shared_examples.rb'
@@ -1644,15 +1644,9 @@ Gitlab/NamespacedClass:
- 'app/models/project_pages_metadatum.rb'
- 'app/models/project_repository.rb'
- 'app/models/project_repository_storage_move.rb'
- - 'app/models/project_services/alerts_service.rb'
- - 'app/models/project_services/alerts_service_data.rb'
- - 'app/models/project_services/chat_notification_service.rb'
- - 'app/models/project_services/mattermost_slash_commands_service.rb'
- 'app/models/project_services/mock_monitoring_service.rb'
- 'app/models/project_services/monitoring_service.rb'
- 'app/models/project_services/prometheus_service.rb'
- - 'app/models/project_services/slack_slash_commands_service.rb'
- - 'app/models/project_services/slash_commands_service.rb'
- 'app/models/project_setting.rb'
- 'app/models/project_snippet.rb'
- 'app/models/project_statistics.rb'
diff --git a/app/assets/javascripts/cycle_analytics/components/path_navigation.vue b/app/assets/javascripts/cycle_analytics/components/path_navigation.vue
index abdc546632f..c1e33f73b13 100644
--- a/app/assets/javascripts/cycle_analytics/components/path_navigation.vue
+++ b/app/assets/javascripts/cycle_analytics/components/path_navigation.vue
@@ -5,6 +5,7 @@ import {
GlDeprecatedSkeletonLoading as GlSkeletonLoading,
GlSafeHtmlDirective as SafeHtml,
} from '@gitlab/ui';
+import Tracking from '~/tracking';
import { OVERVIEW_STAGE_ID } from '../constants';
export default {
@@ -17,6 +18,7 @@ export default {
directives: {
SafeHtml,
},
+ mixins: [Tracking.mixin()],
props: {
loading: {
type: Boolean,
@@ -45,6 +47,14 @@ export default {
hasStageCount({ stageCount = null }) {
return stageCount !== null;
},
+ onSelectStage($event) {
+ this.$emit('selected', $event);
+ this.track('click_path_navigation', {
+ extra: {
+ stage_id: $event.id,
+ },
+ });
+ },
},
popoverOptions: {
triggers: 'hover',
@@ -54,7 +64,7 @@ export default {
</script>
<template>
<gl-skeleton-loading v-if="loading" :lines="2" class="h-auto pt-2 pb-1" />
- <gl-path v-else :key="selectedStage.id" :items="stages" @selected="$emit('selected', $event)">
+ <gl-path v-else :key="selectedStage.id" :items="stages" @selected="onSelectStage">
<template #default="{ pathItem, pathId }">
<gl-popover
v-if="showPopover(pathItem)"
diff --git a/app/helpers/gitlab_script_tag_helper.rb b/app/helpers/gitlab_script_tag_helper.rb
index f784bb69dd8..467f3f7305b 100644
--- a/app/helpers/gitlab_script_tag_helper.rb
+++ b/app/helpers/gitlab_script_tag_helper.rb
@@ -21,12 +21,4 @@ module GitlabScriptTagHelper
super
end
-
- def preload_link_tag(source, options = {})
- # Chrome requires a nonce, see https://gitlab.com/gitlab-org/gitlab/-/issues/331810#note_584964908
- # It's likely to be a browser bug, but we need to work around it anyway
- options[:nonce] = content_security_policy_nonce
-
- super
- end
end
diff --git a/app/models/integrations/base_slash_commands.rb b/app/models/integrations/base_slash_commands.rb
new file mode 100644
index 00000000000..eacf1184aae
--- /dev/null
+++ b/app/models/integrations/base_slash_commands.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+# Base class for ChatOps integrations
+# This class is not meant to be used directly, but only to inherrit from.
+module Integrations
+ class BaseSlashCommands < Integration
+ default_value_for :category, 'chat'
+
+ prop_accessor :token
+
+ has_many :chat_names, foreign_key: :service_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+
+ def valid_token?(token)
+ self.respond_to?(:token) &&
+ self.token.present? &&
+ ActiveSupport::SecurityUtils.secure_compare(token, self.token)
+ end
+
+ def self.supported_events
+ %w()
+ end
+
+ def can_test?
+ false
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'token', placeholder: 'XXxxXXxxXXxxXXxxXXxxXXxx' }
+ ]
+ end
+
+ def trigger(params)
+ return unless valid_token?(params[:token])
+
+ chat_user = find_chat_user(params)
+ user = chat_user&.user
+
+ if user
+ unless user.can?(:use_slash_commands)
+ return Gitlab::SlashCommands::Presenters::Access.new.deactivated if user.deactivated?
+
+ return Gitlab::SlashCommands::Presenters::Access.new.access_denied(project)
+ end
+
+ Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
+ else
+ url = authorize_chat_name_url(params)
+ Gitlab::SlashCommands::Presenters::Access.new(url).authorize
+ end
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ServiceClass
+ def find_chat_user(params)
+ ChatNames::FindUserService.new(self, params).execute
+ end
+ # rubocop: enable CodeReuse/ServiceClass
+
+ # rubocop: disable CodeReuse/ServiceClass
+ def authorize_chat_name_url(params)
+ ChatNames::AuthorizeUserService.new(self, params).execute
+ end
+ # rubocop: enable CodeReuse/ServiceClass
+ end
+end
diff --git a/app/models/integrations/mattermost_slash_commands.rb b/app/models/integrations/mattermost_slash_commands.rb
new file mode 100644
index 00000000000..6cd664da9e7
--- /dev/null
+++ b/app/models/integrations/mattermost_slash_commands.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Integrations
+ class MattermostSlashCommands < BaseSlashCommands
+ include Ci::TriggersHelper
+
+ prop_accessor :token
+
+ def can_test?
+ false
+ end
+
+ def title
+ 'Mattermost slash commands'
+ end
+
+ def description
+ "Perform common tasks with slash commands."
+ end
+
+ def self.to_param
+ 'mattermost_slash_commands'
+ end
+
+ def configure(user, params)
+ token = ::Mattermost::Command.new(user)
+ .create(command(params))
+
+ update(active: true, token: token) if token
+ rescue ::Mattermost::Error => e
+ [false, e.message]
+ end
+
+ def list_teams(current_user)
+ [::Mattermost::Team.new(current_user).all, nil]
+ rescue ::Mattermost::Error => e
+ [[], e.message]
+ end
+
+ def chat_responder
+ ::Gitlab::Chat::Responder::Mattermost
+ end
+
+ private
+
+ def command(params)
+ pretty_project_name = project.full_name
+
+ params.merge(
+ auto_complete: true,
+ auto_complete_desc: "Perform common operations on: #{pretty_project_name}",
+ auto_complete_hint: '[help]',
+ description: "Perform common operations on: #{pretty_project_name}",
+ display_name: "GitLab / #{pretty_project_name}",
+ method: 'P',
+ username: 'GitLab')
+ end
+ end
+end
diff --git a/app/models/integrations/slack_slash_commands.rb b/app/models/integrations/slack_slash_commands.rb
new file mode 100644
index 00000000000..ff1f806df45
--- /dev/null
+++ b/app/models/integrations/slack_slash_commands.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Integrations
+ class SlackSlashCommands < BaseSlashCommands
+ include Ci::TriggersHelper
+
+ def title
+ 'Slack slash commands'
+ end
+
+ def description
+ "Perform common operations in Slack"
+ end
+
+ def self.to_param
+ 'slack_slash_commands'
+ end
+
+ def trigger(params)
+ # Format messages to be Slack-compatible
+ super.tap do |result|
+ result[:text] = format(result[:text]) if result.is_a?(Hash)
+ end
+ end
+
+ def chat_responder
+ ::Gitlab::Chat::Responder::Slack
+ end
+
+ private
+
+ def format(text)
+ ::Slack::Messenger::Util::LinkFormatter.format(text) if text
+ end
+ end
+end
diff --git a/app/models/postgresql/replication_slot.rb b/app/models/postgresql/replication_slot.rb
index c96786423e5..e23a9546cae 100644
--- a/app/models/postgresql/replication_slot.rb
+++ b/app/models/postgresql/replication_slot.rb
@@ -27,7 +27,7 @@ module Postgresql
# We force the use of a transaction here so the query always goes to the
# primary, even when using the EE DB load balancer.
- sizes = transaction { pluck(lag_function) }
+ sizes = transaction { pluck(Arel.sql(lag_function)) }
too_great = sizes.compact.count { |size| size >= max }
# If too many replicas are falling behind too much, the availability of a
diff --git a/app/models/project.rb b/app/models/project.rb
index 9aedd34c993..789a7074938 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -175,6 +175,7 @@ class Project < ApplicationRecord
has_one :jenkins_service, class_name: 'Integrations::Jenkins'
has_one :jira_service, class_name: 'Integrations::Jira'
has_one :mattermost_service, class_name: 'Integrations::Mattermost'
+ has_one :mattermost_slash_commands_service, class_name: 'Integrations::MattermostSlashCommands'
has_one :microsoft_teams_service, class_name: 'Integrations::MicrosoftTeams'
has_one :mock_ci_service, class_name: 'Integrations::MockCi'
has_one :packagist_service, class_name: 'Integrations::Packagist'
@@ -183,12 +184,11 @@ class Project < ApplicationRecord
has_one :pushover_service, class_name: 'Integrations::Pushover'
has_one :redmine_service, class_name: 'Integrations::Redmine'
has_one :slack_service, class_name: 'Integrations::Slack'
+ has_one :slack_slash_commands_service, class_name: 'Integrations::SlackSlashCommands'
has_one :teamcity_service, class_name: 'Integrations::Teamcity'
has_one :unify_circuit_service, class_name: 'Integrations::UnifyCircuit'
has_one :webex_teams_service, class_name: 'Integrations::WebexTeams'
has_one :youtrack_service, class_name: 'Integrations::Youtrack'
- has_one :mattermost_slash_commands_service
- has_one :slack_slash_commands_service
has_one :prometheus_service, inverse_of: :project
has_one :mock_monitoring_service
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
deleted file mode 100644
index 5ee57aa3737..00000000000
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-class MattermostSlashCommandsService < SlashCommandsService
- include Ci::TriggersHelper
-
- prop_accessor :token
-
- def can_test?
- false
- end
-
- def title
- 'Mattermost slash commands'
- end
-
- def description
- "Perform common tasks with slash commands."
- end
-
- def self.to_param
- 'mattermost_slash_commands'
- end
-
- def configure(user, params)
- token = ::Mattermost::Command.new(user)
- .create(command(params))
-
- update(active: true, token: token) if token
- rescue ::Mattermost::Error => e
- [false, e.message]
- end
-
- def list_teams(current_user)
- [::Mattermost::Team.new(current_user).all, nil]
- rescue ::Mattermost::Error => e
- [[], e.message]
- end
-
- def chat_responder
- ::Gitlab::Chat::Responder::Mattermost
- end
-
- private
-
- def command(params)
- pretty_project_name = project.full_name
-
- params.merge(
- auto_complete: true,
- auto_complete_desc: "Perform common operations on: #{pretty_project_name}",
- auto_complete_hint: '[help]',
- description: "Perform common operations on: #{pretty_project_name}",
- display_name: "GitLab / #{pretty_project_name}",
- method: 'P',
- username: 'GitLab')
- end
-end
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
deleted file mode 100644
index 652742c1710..00000000000
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-class SlackSlashCommandsService < SlashCommandsService
- include Ci::TriggersHelper
-
- def title
- 'Slack slash commands'
- end
-
- def description
- "Perform common operations in Slack"
- end
-
- def self.to_param
- 'slack_slash_commands'
- end
-
- def trigger(params)
- # Format messages to be Slack-compatible
- super.tap do |result|
- result[:text] = format(result[:text]) if result.is_a?(Hash)
- end
- end
-
- def chat_responder
- ::Gitlab::Chat::Responder::Slack
- end
-
- private
-
- def format(text)
- ::Slack::Messenger::Util::LinkFormatter.format(text) if text
- end
-end
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
deleted file mode 100644
index 37d16737052..00000000000
--- a/app/models/project_services/slash_commands_service.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-# Base class for Chat services
-# This class is not meant to be used directly, but only to inherrit from.
-class SlashCommandsService < Integration
- default_value_for :category, 'chat'
-
- prop_accessor :token
-
- has_many :chat_names, foreign_key: :service_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
-
- def valid_token?(token)
- self.respond_to?(:token) &&
- self.token.present? &&
- ActiveSupport::SecurityUtils.secure_compare(token, self.token)
- end
-
- def self.supported_events
- %w()
- end
-
- def can_test?
- false
- end
-
- def fields
- [
- { type: 'text', name: 'token', placeholder: 'XXxxXXxxXXxxXXxxXXxxXXxx' }
- ]
- end
-
- def trigger(params)
- return unless valid_token?(params[:token])
-
- chat_user = find_chat_user(params)
- user = chat_user&.user
-
- if user
- unless user.can?(:use_slash_commands)
- return Gitlab::SlashCommands::Presenters::Access.new.deactivated if user.deactivated?
-
- return Gitlab::SlashCommands::Presenters::Access.new.access_denied(project)
- end
-
- Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
- else
- url = authorize_chat_name_url(params)
- Gitlab::SlashCommands::Presenters::Access.new(url).authorize
- end
- end
-
- private
-
- # rubocop: disable CodeReuse/ServiceClass
- def find_chat_user(params)
- ChatNames::FindUserService.new(self, params).execute
- end
- # rubocop: enable CodeReuse/ServiceClass
-
- # rubocop: disable CodeReuse/ServiceClass
- def authorize_chat_name_url(params)
- ChatNames::AuthorizeUserService.new(self, params).execute
- end
- # rubocop: enable CodeReuse/ServiceClass
-end
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index ee2817879be..73a09f00fd6 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -62,13 +62,12 @@
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
- - if Feature.enabled?(:create_cloud_run_clusters, clusterable, default_enabled: true)
- .form-group
- = provider_gcp_field.check_box :cloud_run, { label: s_('ClusterIntegration|Enable Cloud Run for Anthos'),
- label_class: 'label-bold' }
- .form-text.text-muted
- = s_('ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster.')
- = link_to _('More information'), help_page_path('user/project/clusters/add_gke_clusters.md', anchor: 'cloud-run-for-anthos'), target: '_blank'
+ .form-group
+ = provider_gcp_field.check_box :cloud_run, { label: s_('ClusterIntegration|Enable Cloud Run for Anthos'),
+ label_class: 'label-bold' }
+ .form-text.text-muted
+ = s_('ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster.')
+ = link_to _('More information'), help_page_path('user/project/clusters/add_gke_clusters.md', anchor: 'cloud-run-for-anthos'), target: '_blank'
.form-group
= field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'),
diff --git a/config/feature_flags/development/cached_sidebar_open_epics_count.yml b/config/feature_flags/development/cached_sidebar_open_epics_count.yml
deleted file mode 100644
index 265f3135b48..00000000000
--- a/config/feature_flags/development/cached_sidebar_open_epics_count.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: cached_sidebar_open_epics_count
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58064
-rollout_issue_url:
-milestone: '13.11'
-type: development
-group: group::product planning
-default_enabled: true
diff --git a/config/feature_flags/development/create_cloud_run_clusters.yml b/config/feature_flags/development/create_cloud_run_clusters.yml
deleted file mode 100644
index 8352d6290e0..00000000000
--- a/config/feature_flags/development/create_cloud_run_clusters.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: create_cloud_run_clusters
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19063
-rollout_issue_url:
-milestone: '12.5'
-type: development
-group: group::configure
-default_enabled: true
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index 7349052f2e6..c95842e7945 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -34,8 +34,15 @@ feature for CentOS 6, follow [the instructions on how to build and install a cus
## Fast lookup is required for Geo **(PREMIUM)**
-By default, GitLab manages an `authorized_keys` file, which contains all the
-public SSH keys for users allowed to access GitLab. However, to maintain a
+By default, GitLab manages an `authorized_keys` file that is located in the
+`git` user's home directory. For most installations, this will be located under
+`/var/opt/gitlab/.ssh/authorized_keys`, but you can use the following command to locate the `authorized_keys` on your system.:
+
+```shell
+getent passwd git | cut -d: -f6 | awk '{print $1"/.ssh/authorized_keys"}'
+```
+
+The `authorized_keys` file contains all the public SSH keys for users allowed to access GitLab. However, to maintain a
single source of truth, [Geo](../geo/index.md) needs to be configured to perform SSH fingerprint
lookups via database lookup.
@@ -73,7 +80,7 @@ sudo service sshd reload
```
Confirm that SSH is working by commenting out your user's key in the `authorized_keys`
-(start the line with a `#` to comment it), and attempting to pull a repository.
+file (start the line with a `#` to comment it), and attempting to pull a repository.
A successful pull would mean that GitLab was able to find the key in the database,
since it is not present in the file anymore.
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index d544e6441ff..b9509039cb0 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -255,6 +255,27 @@ base template is migrated to use the `rules` syntax.
For users who cannot migrate just yet, you can alternatively pin your templates to
the [GitLab 12.10 based templates](https://gitlab.com/gitlab-org/auto-devops-v12-10).
+## Use images hosted in a local Docker registry
+
+You can configure many Auto DevOps jobs to run in an [offline environment](../../user/application_security/offline_deployments/index.md):
+
+1. Copy the required Auto DevOps Docker images from Docker Hub and `registry.gitlab.com` to their local GitLab container registry.
+1. After the images are hosted and available in a local registry, edit `.gitlab-ci.yml` to point to the locally-hosted images. For example:
+
+ ```yaml
+ include:
+ - template: Auto-DevOps.gitlab-ci.yml
+
+ variables:
+ REGISTRY_URL: "registry.gitlab.example"
+
+ build:
+ image: "$REGISTRY_URL/docker/auto-build-image:v0.6.0"
+ services:
+ - name: "$REGISTRY_URL/greg/docker/docker:20.10.6-dind"
+ command: ['--tls=false', '--host=tcp://0.0.0.0:2375']
+ ```
+
## PostgreSQL database support
To support applications requiring a database,
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index 7bb021b4b1f..89fd32a8db1 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -385,32 +385,7 @@ To remove a child epic from a parent epic:
## Cached epic count
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299540) in GitLab 13.11.
-> - It's [deployed behind a feature flag](../../feature_flags.md), enabled by default.
-> - It's enabled on GitLab.com.
-> - It's recommended for production use.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-cached-epic-count).
-
-WARNING:
-This feature might not be available to you. Check the **version history** note above for details.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/327320) in GitLab 14.0.
In a group, the sidebar displays the total count of open epics and this value is cached if higher
than 1000. The cached value is rounded to thousands (or millions) and updated every 24 hours.
-
-### Enable or disable cached epic count **(PREMIUM SELF)**
-
-Cached epic count in the left sidebar is under development but ready for production use. It is
-deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can disable it.
-
-To disable it:
-
-```ruby
-Feature.disable(:cached_sidebar_open_epics_count)
-```
-
-To enable it:
-
-```ruby
-Feature.enable(:cached_sidebar_open_epics_count)
-```
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 1b9a4629cb8..d69d1a4b1b3 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -10,14 +10,11 @@ Users have different abilities depending on the role they have in a
particular group or project. If a user is both in a project's group and the
project itself, the highest role is used.
-On public and internal projects, the Guest role is not enforced. All users can:
+On [public and internal projects](../api/projects.md#project-visibility-level), the Guest role
+(not to be confused with [Guest user](#free-guest-users)) is not enforced.
-- Create issues.
-- Leave comments.
-- Clone or download the project code.
-
-When a member leaves a team's project, all the assigned [Issues](project/issues/index.md) and [Merge Requests](project/merge_requests/index.md)
-are unassigned automatically.
+When a member leaves a team's project, all the assigned [issues](project/issues/index.md) and
+[merge requests](project/merge_requests/index.md) are automatically unassigned.
GitLab [administrators](../administration/index.md) receive all permissions.
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 2efe6663f3f..ca13ea0789a 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -794,6 +794,7 @@ module API
::Integrations::Jenkins,
::Integrations::Jira,
::Integrations::Mattermost,
+ ::Integrations::MattermostSlashCommands,
::Integrations::MicrosoftTeams,
::Integrations::Packagist,
::Integrations::PipelinesEmail,
@@ -801,10 +802,9 @@ module API
::Integrations::Pushover,
::Integrations::Redmine,
::Integrations::Slack,
+ ::Integrations::SlackSlashCommands,
::Integrations::Teamcity,
::Integrations::Youtrack,
- ::MattermostSlashCommandsService,
- ::SlackSlashCommandsService,
::PrometheusService
]
end
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index 50b49071737..e42b174e085 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -9,7 +9,7 @@ module Gitlab
def self.default_settings_hash
settings_hash = {
- 'enabled' => true,
+ 'enabled' => Rails.env.development? || Rails.env.test?,
'report_only' => false,
'directives' => {
'default_src' => "'self'",
@@ -24,7 +24,7 @@ module Gitlab
'media_src' => "'self'",
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com",
'style_src' => "'self' 'unsafe-inline'",
- 'worker_src' => "'self' blob: data:",
+ 'worker_src' => "'self'",
'object_src' => "'none'",
'report_uri' => nil
}
@@ -37,7 +37,6 @@ module Gitlab
allow_webpack_dev_server(settings_hash) if Rails.env.development?
allow_cdn(settings_hash) if ENV['GITLAB_CDN_HOST'].present?
- allow_snowplow(settings_hash) if Gitlab::CurrentSettings.snowplow_enabled?
settings_hash
end
@@ -80,11 +79,6 @@ module Gitlab
append_to_directive(settings_hash, 'script_src', cdn_host)
append_to_directive(settings_hash, 'style_src', cdn_host)
- append_to_directive(settings_hash, 'font_src', cdn_host)
- end
-
- def self.allow_snowplow(settings_hash)
- append_to_directive(settings_hash, 'connect_src', Gitlab::CurrentSettings.snowplow_collector_hostname)
end
def self.append_to_directive(settings_hash, directive, text)
diff --git a/lib/gitlab/integrations/sti_type.rb b/lib/gitlab/integrations/sti_type.rb
index 644f6f1f804..b25b1250d01 100644
--- a/lib/gitlab/integrations/sti_type.rb
+++ b/lib/gitlab/integrations/sti_type.rb
@@ -6,8 +6,8 @@ module Gitlab
NAMESPACED_INTEGRATIONS = Set.new(%w(
Asana Assembla Bamboo Bugzilla Buildkite Campfire Confluence CustomIssueTracker Datadog
Discord DroneCi EmailsOnPush Ewm ExternalWiki Flowdock HangoutsChat IssueTracker Irker
- Jenkins Jira Mattermost MicrosoftTeams MockCi Packagist PipelinesEmail Pivotaltracker
- Pushover Redmine Slack Teamcity UnifyCircuit Youtrack WebexTeams
+ Jenkins Jira Mattermost MattermostSlashCommands MicrosoftTeams MockCi Packagist PipelinesEmail Pivotaltracker
+ Pushover Redmine Slack SlackSlashCommands Teamcity UnifyCircuit Youtrack WebexTeams
)).freeze
def cast(value)
diff --git a/lib/gitlab/sidekiq_middleware.rb b/lib/gitlab/sidekiq_middleware.rb
index 68faaa8ab2f..30741f29563 100644
--- a/lib/gitlab/sidekiq_middleware.rb
+++ b/lib/gitlab/sidekiq_middleware.rb
@@ -20,6 +20,7 @@ module Gitlab
chain.add ::Gitlab::SidekiqMiddleware::BatchLoader
chain.add ::Labkit::Middleware::Sidekiq::Server
chain.add ::Gitlab::SidekiqMiddleware::InstrumentationLogger
+ chain.add ::Gitlab::Database::LoadBalancing::SidekiqServerMiddleware if load_balancing_enabled?
chain.add ::Gitlab::SidekiqMiddleware::AdminMode::Server
chain.add ::Gitlab::SidekiqVersioning::Middleware
chain.add ::Gitlab::SidekiqStatus::ServerMiddleware
@@ -41,9 +42,13 @@ module Gitlab
# Size limiter should be placed at the bottom, but before the metrics midleware
chain.add ::Gitlab::SidekiqMiddleware::SizeLimiter::Client
chain.add ::Gitlab::SidekiqMiddleware::ClientMetrics
+ chain.add ::Gitlab::Database::LoadBalancing::SidekiqClientMiddleware if load_balancing_enabled?
end
end
+
+ def self.load_balancing_enabled?
+ ::Gitlab::Database::LoadBalancing.enable?
+ end
+ private_class_method :load_balancing_enabled?
end
end
-
-Gitlab::SidekiqMiddleware.singleton_class.prepend_mod_with('Gitlab::SidekiqMiddleware')
diff --git a/lib/sidebars/projects/menus/security_compliance_menu.rb b/lib/sidebars/projects/menus/security_compliance_menu.rb
index 6c9fb8312bd..5616b466560 100644
--- a/lib/sidebars/projects/menus/security_compliance_menu.rb
+++ b/lib/sidebars/projects/menus/security_compliance_menu.rb
@@ -17,7 +17,7 @@ module Sidebars
override :link
def link
- project_security_configuration_path(context.project)
+ renderable_items.first&.link
end
override :title
@@ -33,18 +33,16 @@ module Sidebars
private
def configuration_menu_item
- strong_memoize(:configuration_menu_item) do
- unless render_configuration_menu_item?
- next ::Sidebars::NilMenuItem.new(item_id: :configuration)
- end
-
- ::Sidebars::MenuItem.new(
- title: _('Configuration'),
- link: project_security_configuration_path(context.project),
- active_routes: { path: configuration_menu_item_paths },
- item_id: :configuration
- )
+ unless render_configuration_menu_item?
+ return ::Sidebars::NilMenuItem.new(item_id: :configuration)
end
+
+ ::Sidebars::MenuItem.new(
+ title: _('Configuration'),
+ link: project_security_configuration_path(context.project),
+ active_routes: { path: configuration_menu_item_paths },
+ item_id: :configuration
+ )
end
def render_configuration_menu_item?
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index 019ae19bf72..edec8c3e9c6 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Projects::MattermostsController do
describe 'GET #new' do
before do
- allow_next_instance_of(MattermostSlashCommandsService) do |instance|
+ allow_next_instance_of(Integrations::MattermostSlashCommands) do |instance|
allow(instance).to receive(:list_teams).and_return([])
end
end
@@ -43,7 +43,7 @@ RSpec.describe Projects::MattermostsController do
context 'no request can be made to mattermost' do
it 'shows the error' do
- allow_next_instance_of(MattermostSlashCommandsService) do |instance|
+ allow_next_instance_of(Integrations::MattermostSlashCommands) do |instance|
allow(instance).to receive(:configure).and_return([false, "error message"])
end
diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb
index e8ecc247dc5..71492a6faf6 100644
--- a/spec/factories/integrations.rb
+++ b/spec/factories/integrations.rb
@@ -167,6 +167,12 @@ FactoryBot.define do
type { 'SlackService' }
end
+ factory :slack_slash_commands_service, class: 'Integrations::SlackSlashCommands' do
+ project
+ active { true }
+ type { 'SlackSlashCommandsService' }
+ end
+
factory :pipelines_email_service, class: 'Integrations::PipelinesEmail' do
project
active { true }
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index 84c9b8c6b54..b2ca0424b6d 100644
--- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
@@ -84,7 +84,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js do
end
it 'shows an error alert with the error message if there is an error requesting teams' do
- allow_any_instance_of(MattermostSlashCommandsService).to receive(:list_teams) { [[], 'test mattermost error message'] }
+ allow_any_instance_of(Integrations::MattermostSlashCommands).to receive(:list_teams) { [[], 'test mattermost error message'] }
click_link 'Add to Mattermost'
@@ -113,7 +113,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js do
def stub_teams(count: 0)
teams = create_teams(count)
- allow_any_instance_of(MattermostSlashCommandsService).to receive(:list_teams) { [teams, nil] }
+ allow_any_instance_of(Integrations::MattermostSlashCommands).to receive(:list_teams) { [teams, nil] }
teams
end
diff --git a/spec/frontend/cycle_analytics/path_navigation_spec.js b/spec/frontend/cycle_analytics/path_navigation_spec.js
index 182b76a1453..c6d72d3b571 100644
--- a/spec/frontend/cycle_analytics/path_navigation_spec.js
+++ b/spec/frontend/cycle_analytics/path_navigation_spec.js
@@ -1,11 +1,13 @@
import { GlPath, GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Component from '~/cycle_analytics/components/path_navigation.vue';
import { transformedProjectStagePathData, selectedStage } from './mock_data';
describe('Project PathNavigation', () => {
let wrapper = null;
+ let trackingSpy = null;
const createComponent = (props) => {
return extendedWrapper(
@@ -43,9 +45,11 @@ describe('Project PathNavigation', () => {
beforeEach(() => {
wrapper = createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
afterEach(() => {
+ unmockTracking();
wrapper.destroy();
wrapper = null;
});
@@ -132,5 +136,13 @@ describe('Project PathNavigation', () => {
[transformedProjectStagePathData[2]],
]);
});
+
+ it('sends tracking information', () => {
+ clickItemAt(0);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_path_navigation', {
+ extra: { stage_id: selectedStage.slug },
+ });
+ });
});
});
diff --git a/spec/helpers/gitlab_script_tag_helper_spec.rb b/spec/helpers/gitlab_script_tag_helper_spec.rb
index 35f2c0795be..37413b9b1c2 100644
--- a/spec/helpers/gitlab_script_tag_helper_spec.rb
+++ b/spec/helpers/gitlab_script_tag_helper_spec.rb
@@ -41,11 +41,4 @@ RSpec.describe GitlabScriptTagHelper do
expect(helper.javascript_tag( '// ignored', type: 'application/javascript') { 'alert(1)' }.to_s).to eq tag_with_nonce_and_type
end
end
-
- describe '#preload_link_tag' do
- it 'returns a link tag with a nonce' do
- expect(helper.preload_link_tag('https://example.com/script.js').to_s)
- .to eq "<link rel=\"preload\" href=\"https://example.com/script.js\" as=\"script\" type=\"text/javascript\" nonce=\"noncevalue\">"
- end
- end
end
diff --git a/spec/helpers/webpack_helper_spec.rb b/spec/helpers/webpack_helper_spec.rb
index f9e2d265153..f9386c99dc3 100644
--- a/spec/helpers/webpack_helper_spec.rb
+++ b/spec/helpers/webpack_helper_spec.rb
@@ -15,7 +15,6 @@ RSpec.describe WebpackHelper do
describe '#webpack_preload_asset_tag' do
before do
allow(Gitlab::Webpack::Manifest).to receive(:asset_paths).and_return([asset_path])
- allow(helper).to receive(:content_security_policy_nonce).and_return('noncevalue')
end
it 'preloads the resource by default' do
@@ -23,7 +22,7 @@ RSpec.describe WebpackHelper do
output = helper.webpack_preload_asset_tag(source)
- expect(output).to eq("<link rel=\"preload\" href=\"#{asset_path}\" as=\"script\" type=\"text/javascript\" nonce=\"noncevalue\">")
+ expect(output).to eq("<link rel=\"preload\" href=\"#{asset_path}\" as=\"script\" type=\"text/javascript\">")
end
it 'prefetches the resource if explicitly asked' do
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index 9e6bf9db907..19e52d2cf4a 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -20,9 +20,9 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
describe '.default_settings_hash' do
- it 'returns defaults for all keys' do
- settings = described_class.default_settings_hash
+ let(:settings) { described_class.default_settings_hash }
+ it 'returns defaults for all keys' do
expect(settings['enabled']).to be_truthy
expect(settings['report_only']).to be_falsey
@@ -38,32 +38,26 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(directives['child_src']).to eq(directives['frame_src'])
end
- context 'when GITLAB_CDN_HOST is set' do
+ context 'when in production' do
before do
- stub_env('GITLAB_CDN_HOST', 'https://example.com')
+ allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production'))
end
- it 'adds GITLAB_CDN_HOST to CSP' do
- settings = described_class.default_settings_hash
- directives = settings['directives']
-
- expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com https://example.com")
- expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
- expect(directives['font_src']).to eq("'self' https://example.com")
+ it 'is disabled' do
+ expect(settings['enabled']).to be_falsey
end
end
- context 'when snowplow is configured' do
+ context 'when GITLAB_CDN_HOST is set' do
before do
- stub_application_setting(snowplow_enabled: true)
- stub_application_setting(snowplow_collector_hostname: 'snowplow.example.com')
+ stub_env('GITLAB_CDN_HOST', 'https://example.com')
end
- it 'adds snowplow to CSP' do
- settings = described_class.default_settings_hash
+ it 'adds GITLAB_CDN_HOST to CSP' do
directives = settings['directives']
- expect(directives['connect_src']).to eq("'self' snowplow.example.com")
+ expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com https://example.com")
+ expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb
index 0efdef0c999..5e4e79e818e 100644
--- a/spec/lib/gitlab/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb
@@ -4,215 +4,212 @@ require 'spec_helper'
require 'sidekiq/testing'
RSpec.describe Gitlab::SidekiqMiddleware do
- before do
- stub_const('TestWorker', Class.new)
+ let(:job_args) { [0.01] }
+ let(:disabled_sidekiq_middlewares) { [] }
+ let(:chain) { Sidekiq::Middleware::Chain.new }
+ let(:queue) { 'test' }
+ let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
+ let(:worker_class) do
+ Class.new do
+ def self.name
+ 'TestWorker'
+ end
- TestWorker.class_eval do
- include Sidekiq::Worker
include ApplicationWorker
- def perform(_arg)
+ def perform(*args)
Gitlab::SafeRequestStore['gitaly_call_actual'] = 1
Gitlab::SafeRequestStore[:gitaly_query_time] = 5
end
end
end
- around do |example|
- Sidekiq::Testing.inline! { example.run }
+ before do
+ stub_const('TestWorker', worker_class)
end
- let(:worker_class) { TestWorker }
- let(:job_args) { [0.01] }
-
- # The test sets up a new server middleware stack, ensuring that the
- # appropriate middlewares, as passed into server_configurator,
- # are invoked.
- # Additionally the test ensure that each middleware is
- # 1) not failing
- # 2) yielding exactly once
- describe '.server_configurator' do
- around do |example|
- with_sidekiq_server_middleware do |chain|
- described_class.server_configurator(
- metrics: metrics,
- arguments_logger: arguments_logger,
- memory_killer: memory_killer
- ).call(chain)
+ shared_examples "a middleware chain" do |load_balancing_enabled|
+ before do
+ allow(::Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(load_balancing_enabled)
+ configurator.call(chain)
+ end
- example.run
+ it "passes through the right middlewares", :aggregate_failures do
+ enabled_sidekiq_middlewares.each do |middleware|
+ expect_next_instances_of(middleware, 1, true) do |middleware_instance|
+ expect(middleware_instance).to receive(:call).with(*middleware_expected_args).once.and_call_original
+ end
end
+
+ expect { |b| chain.invoke(*worker_args, &b) }.to yield_control.once
end
+ end
+
+ shared_examples "a middleware chain for mailer" do |load_balancing_enabled|
+ let(:worker_class) { ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper }
- let(:middleware_expected_args) { [a_kind_of(worker_class), hash_including({ 'args' => job_args }), anything] }
+ it_behaves_like "a middleware chain", load_balancing_enabled
+ end
+
+ describe '.server_configurator' do
+ let(:configurator) { described_class.server_configurator }
+ let(:worker_args) { [worker_class.new, { 'args' => job_args }, queue] }
+ let(:middleware_expected_args) { [a_kind_of(worker_class), hash_including({ 'args' => job_args }), queue] }
let(:all_sidekiq_middlewares) do
[
- Gitlab::SidekiqMiddleware::Monitor,
- Gitlab::SidekiqMiddleware::BatchLoader,
- Labkit::Middleware::Sidekiq::Server,
- Gitlab::SidekiqMiddleware::InstrumentationLogger,
- Gitlab::SidekiqVersioning::Middleware,
- Gitlab::SidekiqStatus::ServerMiddleware,
- Gitlab::SidekiqMiddleware::ServerMetrics,
- Gitlab::SidekiqMiddleware::ArgumentsLogger,
- Gitlab::SidekiqMiddleware::MemoryKiller,
- Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
- Gitlab::SidekiqMiddleware::ExtraDoneLogMetadata,
- Gitlab::SidekiqMiddleware::WorkerContext::Server,
- Gitlab::SidekiqMiddleware::AdminMode::Server,
- Gitlab::SidekiqMiddleware::DuplicateJobs::Server
+ ::Gitlab::SidekiqMiddleware::Monitor,
+ ::Gitlab::SidekiqMiddleware::ServerMetrics,
+ ::Gitlab::SidekiqMiddleware::ArgumentsLogger,
+ ::Gitlab::SidekiqMiddleware::MemoryKiller,
+ ::Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
+ ::Gitlab::SidekiqMiddleware::ExtraDoneLogMetadata,
+ ::Gitlab::SidekiqMiddleware::BatchLoader,
+ ::Labkit::Middleware::Sidekiq::Server,
+ ::Gitlab::SidekiqMiddleware::InstrumentationLogger,
+ ::Gitlab::Database::LoadBalancing::SidekiqServerMiddleware,
+ ::Gitlab::SidekiqMiddleware::AdminMode::Server,
+ ::Gitlab::SidekiqVersioning::Middleware,
+ ::Gitlab::SidekiqStatus::ServerMiddleware,
+ ::Gitlab::SidekiqMiddleware::WorkerContext::Server,
+ ::Gitlab::SidekiqMiddleware::DuplicateJobs::Server
]
end
- let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
+ describe "server metrics" do
+ around do |example|
+ with_sidekiq_server_middleware do |chain|
+ described_class.server_configurator(
+ metrics: true,
+ arguments_logger: true,
+ memory_killer: true
+ ).call(chain)
- shared_examples "a server middleware chain" do
- it "passes through the right server middlewares" do
- enabled_sidekiq_middlewares.each do |middleware|
- expect_next_instance_of(middleware) do |middleware_instance|
- expect(middleware_instance).to receive(:call).with(*middleware_expected_args).once.and_call_original
- end
+ Sidekiq::Testing.inline! { example.run }
end
+ end
+ let(:gitaly_histogram) { double(:gitaly_histogram) }
- disabled_sidekiq_middlewares.each do |middleware|
- expect(middleware).not_to receive(:new)
- end
+ before do
+ allow(Gitlab::Metrics).to receive(:histogram).and_call_original
+
+ allow(Gitlab::Metrics).to receive(:histogram)
+ .with(:sidekiq_jobs_gitaly_seconds, anything, anything, anything)
+ .and_return(gitaly_histogram)
+ end
+
+ it "records correct Gitaly duration" do
+ expect(gitaly_histogram).to receive(:observe).with(anything, 5.0)
worker_class.perform_async(*job_args)
end
end
- shared_examples "a server middleware chain for mailer" do
- let(:worker_class) { ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper }
- let(:job_args) do
- [
- {
- "job_class" => "ActionMailer::MailDeliveryJob",
- "job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e",
- "provider_job_id" => nil,
- "queue_name" => "mailers",
- "priority" => nil,
- "arguments" => [
- "Notify",
- "test_email",
- "deliver_now",
- {
- "args" => [
- "test@example.com",
- "subject",
- "body"
- ],
- ActiveJob::Arguments.const_get('RUBY2_KEYWORDS_KEY', false) => ["args"]
- }
- ],
- "executions" => 0,
- "exception_executions" => {},
- "locale" => "en",
- "timezone" => "UTC",
- "enqueued_at" => "2020-07-27T07:43:31Z"
- }
- ]
+ context "all optional middlewares on" do
+ context "when load balancing is enabled" do
+ before do
+ allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer, :release_host)
+ end
+
+ it_behaves_like "a middleware chain", true
+ it_behaves_like "a middleware chain for mailer", true
end
- it_behaves_like "a server middleware chain"
- end
+ context "when load balancing is disabled" do
+ let(:disabled_sidekiq_middlewares) do
+ [
+ Gitlab::Database::LoadBalancing::SidekiqServerMiddleware
+ ]
+ end
- context "all optional middlewares off" do
- let(:metrics) { false }
- let(:arguments_logger) { false }
- let(:memory_killer) { false }
- let(:disabled_sidekiq_middlewares) do
- [
- Gitlab::SidekiqMiddleware::ServerMetrics,
- Gitlab::SidekiqMiddleware::ArgumentsLogger,
- Gitlab::SidekiqMiddleware::MemoryKiller
- ]
+ it_behaves_like "a middleware chain", false
+ it_behaves_like "a middleware chain for mailer", false
end
-
- it_behaves_like "a server middleware chain"
- it_behaves_like "a server middleware chain for mailer"
end
- context "all optional middlewares on" do
- let(:metrics) { true }
- let(:arguments_logger) { true }
- let(:memory_killer) { true }
- let(:disabled_sidekiq_middlewares) { [] }
-
- it_behaves_like "a server middleware chain"
- it_behaves_like "a server middleware chain for mailer"
+ context "all optional middlewares off" do
+ let(:configurator) do
+ described_class.server_configurator(
+ metrics: false,
+ arguments_logger: false,
+ memory_killer: false
+ )
+ end
- context "server metrics" do
- let(:gitaly_histogram) { double(:gitaly_histogram) }
+ context "when load balancing is enabled" do
+ let(:disabled_sidekiq_middlewares) do
+ [
+ Gitlab::SidekiqMiddleware::ServerMetrics,
+ Gitlab::SidekiqMiddleware::ArgumentsLogger,
+ Gitlab::SidekiqMiddleware::MemoryKiller
+ ]
+ end
before do
- allow(Gitlab::Metrics).to receive(:histogram).and_call_original
-
- allow(Gitlab::Metrics).to receive(:histogram)
- .with(:sidekiq_jobs_gitaly_seconds, anything, anything, anything)
- .and_return(gitaly_histogram)
+ allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer, :release_host)
end
- it "records correct Gitaly duration" do
- expect(gitaly_histogram).to receive(:observe).with(anything, 5.0)
+ it_behaves_like "a middleware chain", true
+ it_behaves_like "a middleware chain for mailer", true
+ end
- worker_class.perform_async(*job_args)
+ context "when load balancing is disabled" do
+ let(:disabled_sidekiq_middlewares) do
+ [
+ Gitlab::SidekiqMiddleware::ServerMetrics,
+ Gitlab::SidekiqMiddleware::ArgumentsLogger,
+ Gitlab::SidekiqMiddleware::MemoryKiller,
+ Gitlab::Database::LoadBalancing::SidekiqServerMiddleware
+ ]
end
+
+ it_behaves_like "a middleware chain", false
+ it_behaves_like "a middleware chain for mailer", false
end
end
end
- # The test sets up a new client middleware stack. The test ensures
- # that each middleware is:
- # 1) not failing
- # 2) yielding exactly once
describe '.client_configurator' do
- let(:chain) { Sidekiq::Middleware::Chain.new }
- let(:job) { { 'args' => job_args } }
- let(:queue) { 'default' }
+ let(:configurator) { described_class.client_configurator }
let(:redis_pool) { Sidekiq.redis_pool }
- let(:middleware_expected_args) { [worker_class_arg, job, queue, redis_pool] }
- let(:expected_middlewares) do
+ let(:middleware_expected_args) { [worker_class, hash_including({ 'args' => job_args }), queue, redis_pool] }
+ let(:worker_args) { [worker_class, { 'args' => job_args }, queue, redis_pool] }
+ let(:all_sidekiq_middlewares) do
[
- ::Gitlab::SidekiqMiddleware::WorkerContext::Client,
- ::Labkit::Middleware::Sidekiq::Client,
- ::Gitlab::SidekiqMiddleware::DuplicateJobs::Client,
- ::Gitlab::SidekiqStatus::ClientMiddleware,
- ::Gitlab::SidekiqMiddleware::AdminMode::Client,
- ::Gitlab::SidekiqMiddleware::SizeLimiter::Client,
- ::Gitlab::SidekiqMiddleware::ClientMetrics
+ ::Gitlab::SidekiqMiddleware::WorkerContext::Client,
+ ::Labkit::Middleware::Sidekiq::Client,
+ ::Gitlab::SidekiqMiddleware::DuplicateJobs::Client,
+ ::Gitlab::SidekiqStatus::ClientMiddleware,
+ ::Gitlab::SidekiqMiddleware::AdminMode::Client,
+ ::Gitlab::SidekiqMiddleware::SizeLimiter::Client,
+ ::Gitlab::SidekiqMiddleware::ClientMetrics,
+ ::Gitlab::Database::LoadBalancing::SidekiqClientMiddleware
]
end
- before do
- described_class.client_configurator.call(chain)
- end
-
- shared_examples "a client middleware chain" do
- # Its possible that a middleware could accidentally omit a yield call
- # this will prevent the full middleware chain from being executed.
- # This test ensures that this does not happen
- it "invokes the chain" do
- expected_middlewares do |middleware|
- expect_any_instance_of(middleware).to receive(:call).with(*middleware_expected_args).once.ordered.and_call_original
- end
-
- expect { |b| chain.invoke(worker_class_arg, job, queue, redis_pool, &b) }.to yield_control.once
+ context "when load balancing is disabled" do
+ let(:disabled_sidekiq_middlewares) do
+ [
+ Gitlab::Database::LoadBalancing::SidekiqClientMiddleware
+ ]
end
- end
- # Sidekiq documentation states that the worker class could be a string
- # or a class reference. We should test for both
- context "handles string worker_class values" do
- let(:worker_class_arg) { worker_class.to_s }
+ it_behaves_like "a middleware chain", false
+ it_behaves_like "a middleware chain for mailer", false
- it_behaves_like "a client middleware chain"
- end
+ # Sidekiq documentation states that the worker class could be a string
+ # or a class reference. We should test for both
+ context "worker_class as string value" do
+ let(:worker_args) { [worker_class.to_s, { 'args' => job_args }, queue, redis_pool] }
+ let(:middleware_expected_args) { [worker_class.to_s, hash_including({ 'args' => job_args }), queue, redis_pool] }
- context "handles string worker_class values" do
- let(:worker_class_arg) { worker_class }
+ it_behaves_like "a middleware chain", false
+ it_behaves_like "a middleware chain for mailer", false
+ end
+ end
- it_behaves_like "a client middleware chain"
+ context "when load balancing is enabled" do
+ it_behaves_like "a middleware chain", true
+ it_behaves_like "a middleware chain for mailer", true
end
end
end
diff --git a/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb b/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
new file mode 100644
index 00000000000..6e84beeb274
--- /dev/null
+++ b/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Projects::Menus::SecurityComplianceMenu do
+ let_it_be(:project) { create(:project) }
+
+ let(:user) { project.owner }
+ let(:show_promotions) { true }
+ let(:show_discover_project_security) { true }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_promotions: show_promotions, show_discover_project_security: show_discover_project_security) }
+
+ describe 'render?' do
+ subject { described_class.new(context).render? }
+
+ context 'when user is not authenticated' do
+ let(:user) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when user is authenticated' do
+ context 'when the Security & Compliance is disabled' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :access_security_and_compliance, project).and_return(false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when the Security & Compliance is not disabled' do
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/integrations/mattermost_slash_commands_spec.rb
index bbe985bfe0c..c8a6584591c 100644
--- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb
+++ b/spec/models/integrations/mattermost_slash_commands_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe MattermostSlashCommandsService do
- it_behaves_like "chat slash commands service"
+RSpec.describe Integrations::MattermostSlashCommands do
+ it_behaves_like Integrations::BaseSlashCommands
context 'Mattermost API' do
let(:project) { create(:project) }
diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/integrations/slack_slash_commands_spec.rb
index 95c87ef01bc..a9d3c820a3c 100644
--- a/spec/models/project_services/slack_slash_commands_service_spec.rb
+++ b/spec/models/integrations/slack_slash_commands_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe SlackSlashCommandsService do
- it_behaves_like "chat slash commands service"
+RSpec.describe Integrations::SlackSlashCommands do
+ it_behaves_like Integrations::BaseSlashCommands
describe '#trigger' do
context 'when an auth url is generated' do
diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb
index 02a4d783b84..4bad8a3f0c0 100644
--- a/spec/models/postgresql/replication_slot_spec.rb
+++ b/spec/models/postgresql/replication_slot_spec.rb
@@ -24,6 +24,10 @@ RSpec.describe Postgresql::ReplicationSlot do
expect(described_class).to receive(:in_use?).and_return(true)
end
+ it 'does not raise an exception' do
+ expect { described_class.lag_too_great? }.not_to raise_error
+ end
+
it 'returns true when replication lag is too great' do
expect(described_class)
.to receive(:pluck)
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index 89c9d742033..c00b7203af6 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -18,8 +18,8 @@ RSpec.shared_context 'project navbar structure' do
{
nav_item: _('Security & Compliance'),
nav_sub_items: [
- _('Configuration'),
- (_('Audit Events') if Gitlab.ee?)
+ (_('Audit Events') if Gitlab.ee?),
+ _('Configuration')
]
}
end
diff --git a/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
index 49729afce61..128999d02fa 100644
--- a/spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'chat slash commands service' do
+RSpec.shared_examples Integrations::BaseSlashCommands do
describe "Associations" do
it { is_expected.to respond_to :token }
it { is_expected.to have_many :chat_names }
diff --git a/spec/views/projects/clusters/clusters/gcp/_form.html.haml_spec.rb b/spec/views/projects/clusters/clusters/gcp/_form.html.haml_spec.rb
index bf5cb6fb25d..5120998ded6 100644
--- a/spec/views/projects/clusters/clusters/gcp/_form.html.haml_spec.rb
+++ b/spec/views/projects/clusters/clusters/gcp/_form.html.haml_spec.rb
@@ -23,16 +23,4 @@ RSpec.describe 'clusters/clusters/gcp/_form' do
expect(rendered).to have_selector("input[id='cluster_provider_gcp_attributes_cloud_run']")
end
end
-
- context 'with cloud run feature flag disabled' do
- before do
- stub_feature_flags(create_cloud_run_clusters: false)
- end
-
- it 'does not have a cloud run checkbox' do
- render
-
- expect(rendered).not_to have_selector("input[id='cluster_provider_gcp_attributes_cloud_run']")
- end
- end
end
diff --git a/workhorse/doc/operations/configuration.md b/workhorse/doc/operations/configuration.md
index a78c2ac5459..8694cf1bd82 100644
--- a/workhorse/doc/operations/configuration.md
+++ b/workhorse/doc/operations/configuration.md
@@ -103,15 +103,11 @@ Optional fields are as follows:
```
[redis]
DB = 0
-ReadTimeout = "1s"
-KeepAlivePeriod = "5m"
MaxIdle = 1
MaxActive = 1
```
- `DB` is the Database to connect to. Defaults to `0`
-- `ReadTimeout` is how long a redis read-command can take. Defaults to `1s`
-- `KeepAlivePeriod` is how long the redis connection is to be kept alive without anything flowing through it. Defaults to `5m`
- `MaxIdle` is how many idle connections can be in the redis-pool at once. Defaults to 1
- `MaxActive` is how many connections the pool can keep. Defaults to 1
diff --git a/workhorse/internal/config/config.go b/workhorse/internal/config/config.go
index 84849c72744..d019a7710bf 100644
--- a/workhorse/internal/config/config.go
+++ b/workhorse/internal/config/config.go
@@ -70,16 +70,13 @@ type AzureCredentials struct {
}
type RedisConfig struct {
- URL TomlURL
- Sentinel []TomlURL
- SentinelMaster string
- Password string
- DB *int
- ReadTimeout *TomlDuration
- WriteTimeout *TomlDuration
- KeepAlivePeriod *TomlDuration
- MaxIdle *int
- MaxActive *int
+ URL TomlURL
+ Sentinel []TomlURL
+ SentinelMaster string
+ Password string
+ DB *int
+ MaxIdle *int
+ MaxActive *int
}
type ImageResizerConfig struct {
diff --git a/workhorse/internal/redis/redis.go b/workhorse/internal/redis/redis.go
index 0029a2a9e2b..b11a8184bca 100644
--- a/workhorse/internal/redis/redis.go
+++ b/workhorse/internal/redis/redis.go
@@ -113,21 +113,9 @@ var poolDialFunc func() (redis.Conn, error)
var workerDialFunc func() (redis.Conn, error)
func timeoutDialOptions(cfg *config.RedisConfig) []redis.DialOption {
- readTimeout := defaultReadTimeout
- writeTimeout := defaultWriteTimeout
-
- if cfg != nil {
- if cfg.ReadTimeout != nil {
- readTimeout = cfg.ReadTimeout.Duration
- }
-
- if cfg.WriteTimeout != nil {
- writeTimeout = cfg.WriteTimeout.Duration
- }
- }
return []redis.DialOption{
- redis.DialReadTimeout(readTimeout),
- redis.DialWriteTimeout(writeTimeout),
+ redis.DialReadTimeout(defaultReadTimeout),
+ redis.DialWriteTimeout(defaultWriteTimeout),
}
}
@@ -148,47 +136,45 @@ func dialOptionsBuilder(cfg *config.RedisConfig, setTimeouts bool) []redis.DialO
return dopts
}
-func keepAliveDialer(timeout time.Duration) func(string, string) (net.Conn, error) {
- return func(network, address string) (net.Conn, error) {
- addr, err := net.ResolveTCPAddr(network, address)
- if err != nil {
- return nil, err
- }
- tc, err := net.DialTCP(network, nil, addr)
- if err != nil {
- return nil, err
- }
- if err := tc.SetKeepAlive(true); err != nil {
- return nil, err
- }
- if err := tc.SetKeepAlivePeriod(timeout); err != nil {
- return nil, err
- }
- return tc, nil
+func keepAliveDialer(network, address string) (net.Conn, error) {
+ addr, err := net.ResolveTCPAddr(network, address)
+ if err != nil {
+ return nil, err
}
+ tc, err := net.DialTCP(network, nil, addr)
+ if err != nil {
+ return nil, err
+ }
+ if err := tc.SetKeepAlive(true); err != nil {
+ return nil, err
+ }
+ if err := tc.SetKeepAlivePeriod(defaultKeepAlivePeriod); err != nil {
+ return nil, err
+ }
+ return tc, nil
}
type redisDialerFunc func() (redis.Conn, error)
-func sentinelDialer(dopts []redis.DialOption, keepAlivePeriod time.Duration) redisDialerFunc {
+func sentinelDialer(dopts []redis.DialOption) redisDialerFunc {
return func() (redis.Conn, error) {
address, err := sntnl.MasterAddr()
if err != nil {
errorCounter.WithLabelValues("master", "sentinel").Inc()
return nil, err
}
- dopts = append(dopts, redis.DialNetDial(keepAliveDialer(keepAlivePeriod)))
+ dopts = append(dopts, redis.DialNetDial(keepAliveDialer))
return redisDial("tcp", address, dopts...)
}
}
-func defaultDialer(dopts []redis.DialOption, keepAlivePeriod time.Duration, url url.URL) redisDialerFunc {
+func defaultDialer(dopts []redis.DialOption, url url.URL) redisDialerFunc {
return func() (redis.Conn, error) {
if url.Scheme == "unix" {
return redisDial(url.Scheme, url.Path, dopts...)
}
- dopts = append(dopts, redis.DialNetDial(keepAliveDialer(keepAlivePeriod)))
+ dopts = append(dopts, redis.DialNetDial(keepAliveDialer))
// redis.DialURL only works with redis[s]:// URLs
if url.Scheme == "redis" || url.Scheme == "rediss" {
@@ -231,15 +217,11 @@ func countDialer(dialer redisDialerFunc) redisDialerFunc {
// DefaultDialFunc should always used. Only exception is for unit-tests.
func DefaultDialFunc(cfg *config.RedisConfig, setReadTimeout bool) func() (redis.Conn, error) {
- keepAlivePeriod := defaultKeepAlivePeriod
- if cfg.KeepAlivePeriod != nil {
- keepAlivePeriod = cfg.KeepAlivePeriod.Duration
- }
dopts := dialOptionsBuilder(cfg, setReadTimeout)
if sntnl != nil {
- return countDialer(sentinelDialer(dopts, keepAlivePeriod))
+ return countDialer(sentinelDialer(dopts))
}
- return countDialer(defaultDialer(dopts, keepAlivePeriod, cfg.URL.URL))
+ return countDialer(defaultDialer(dopts, cfg.URL.URL))
}
// Configure redis-connection
diff --git a/workhorse/internal/redis/redis_test.go b/workhorse/internal/redis/redis_test.go
index f4b4120517d..eee2f99bbbf 100644
--- a/workhorse/internal/redis/redis_test.go
+++ b/workhorse/internal/redis/redis_test.go
@@ -96,13 +96,11 @@ func TestConfigureMinimalConfig(t *testing.T) {
func TestConfigureFullConfig(t *testing.T) {
i, a := 4, 10
- r := config.TomlDuration{Duration: 3}
cfg := &config.RedisConfig{
- URL: config.TomlURL{},
- Password: "",
- MaxIdle: &i,
- MaxActive: &a,
- ReadTimeout: &r,
+ URL: config.TomlURL{},
+ Password: "",
+ MaxIdle: &i,
+ MaxActive: &a,
}
Configure(cfg, DefaultDialFunc)
@@ -219,11 +217,7 @@ func TestDialOptionsBuildersSetTimeouts(t *testing.T) {
}
func TestDialOptionsBuildersSetTimeoutsConfig(t *testing.T) {
- cfg := &config.RedisConfig{
- ReadTimeout: &config.TomlDuration{Duration: time.Second * time.Duration(15)},
- WriteTimeout: &config.TomlDuration{Duration: time.Second * time.Duration(15)},
- }
- dopts := dialOptionsBuilder(cfg, true)
+ dopts := dialOptionsBuilder(nil, true)
require.Equal(t, 2, len(dopts))
}