summaryrefslogtreecommitdiff
path: root/app/services
diff options
context:
space:
mode:
Diffstat (limited to 'app/services')
-rw-r--r--app/services/application_settings/update_service.rb9
-rw-r--r--app/services/auth/container_registry_authentication_service.rb10
-rw-r--r--app/services/ci/play_build_service.rb4
-rw-r--r--app/services/ci/process_pipeline_service.rb88
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/services/clusters/refresh_service.rb40
-rw-r--r--app/services/cohorts_service.rb6
-rw-r--r--app/services/groups/nested_create_service.rb4
-rw-r--r--app/services/groups/transfer_service.rb1
-rw-r--r--app/services/issuable_base_service.rb1
-rw-r--r--app/services/issues/update_service.rb2
-rw-r--r--app/services/members/destroy_service.rb2
-rw-r--r--app/services/merge_requests/build_service.rb28
-rw-r--r--app/services/merge_requests/mergeability_check_service.rb45
-rw-r--r--app/services/metrics/dashboard/base_service.rb70
-rw-r--r--app/services/metrics/dashboard/default_embed_service.rb63
-rw-r--r--app/services/metrics/dashboard/project_dashboard_service.rb48
-rw-r--r--app/services/metrics/dashboard/system_dashboard_service.rb47
-rw-r--r--app/services/notes/create_service.rb4
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/projects/base_move_relations_service.rb12
-rw-r--r--app/services/projects/destroy_service.rb13
-rw-r--r--app/services/projects/fetch_statistics_increment_service.rb7
-rw-r--r--app/services/projects/move_deploy_keys_projects_service.rb3
-rw-r--r--app/services/projects/move_lfs_objects_projects_service.rb3
-rw-r--r--app/services/projects/move_notification_settings_service.rb3
-rw-r--r--app/services/projects/move_project_authorizations_service.rb3
-rw-r--r--app/services/projects/move_project_group_links_service.rb3
-rw-r--r--app/services/projects/move_project_members_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb31
-rw-r--r--app/services/self_monitoring/project/create_service.rb32
-rw-r--r--app/services/system_note_service.rb8
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb8
-rw-r--r--app/services/web_hook_service.rb6
-rw-r--r--app/services/zoom_notes_service.rb42
35 files changed, 514 insertions, 138 deletions
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 7eeaf8aade1..471df6e2d0c 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -15,6 +15,8 @@ module ApplicationSettings
update_terms(@params.delete(:terms))
+ add_to_outbound_local_requests_whitelist(@params.delete(:add_to_outbound_local_requests_whitelist))
+
if params.key?(:performance_bar_allowed_group_path)
params[:performance_bar_allowed_group_id] = performance_bar_allowed_group_id
end
@@ -32,6 +34,13 @@ module ApplicationSettings
params.key?(:usage_ping_enabled) || params.key?(:version_check_enabled)
end
+ def add_to_outbound_local_requests_whitelist(values)
+ values_array = Array(values).reject(&:empty?)
+ return if values_array.empty?
+
+ @application_setting.add_to_outbound_local_requests_whitelist(values_array)
+ end
+
def update_terms(terms)
return unless terms.present?
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 707caee482c..0a069320936 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -17,6 +17,14 @@ module Auth
end
def self.full_access_token(*names)
+ access_token(%w(*), names)
+ end
+
+ def self.pull_access_token(*names)
+ access_token(['pull'], names)
+ end
+
+ def self.access_token(actions, names)
names = names.flatten
registry = Gitlab.config.registry
token = JSONWebToken::RSAToken.new(registry.key)
@@ -25,7 +33,7 @@ module Auth
token.expire_time = token_expire_at
token[:access] = names.map do |name|
- { type: 'repository', name: name, actions: %w(*) }
+ { type: 'repository', name: name, actions: actions }
end
token.encoded
diff --git a/app/services/ci/play_build_service.rb b/app/services/ci/play_build_service.rb
index eb0b070657d..9f922ffde81 100644
--- a/app/services/ci/play_build_service.rb
+++ b/app/services/ci/play_build_service.rb
@@ -2,7 +2,7 @@
module Ci
class PlayBuildService < ::BaseService
- def execute(build)
+ def execute(build, job_variables_attributes = nil)
unless can?(current_user, :update_build, build)
raise Gitlab::Access::AccessDeniedError
end
@@ -10,7 +10,7 @@ module Ci
# Try to enqueue the build, otherwise create a duplicate.
#
if build.enqueue
- build.tap { |action| action.update(user: current_user) }
+ build.tap { |action| action.update(user: current_user, job_variables_attributes: job_variables_attributes || []) }
else
Ci::Build.retry(build, current_user)
end
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index aaf56048b5c..99d4ff9ecd1 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -4,35 +4,69 @@ module Ci
class ProcessPipelineService < BaseService
attr_reader :pipeline
- def execute(pipeline)
+ def execute(pipeline, trigger_build_ids = nil)
@pipeline = pipeline
update_retried
- new_builds =
- stage_indexes_of_created_processables.map do |index|
- process_stage(index)
- end
+ success = process_stages_without_needs
+
+ # we evaluate dependent needs,
+ # only when the another job has finished
+ success = process_builds_with_needs(trigger_build_ids) || success
@pipeline.update_status
- new_builds.flatten.any?
+ success
end
private
- def process_stage(index)
+ def process_stages_without_needs
+ stage_indexes_of_created_processables_without_needs.flat_map do |index|
+ process_stage_without_needs(index)
+ end.any?
+ end
+
+ def process_stage_without_needs(index)
current_status = status_for_prior_stages(index)
- return if HasStatus::BLOCKED_STATUS.include?(current_status)
+ return unless HasStatus::COMPLETED_STATUSES.include?(current_status)
- if HasStatus::COMPLETED_STATUSES.include?(current_status)
- created_processables_in_stage(index).select do |build|
- Gitlab::OptimisticLocking.retry_lock(build) do |subject|
- Ci::ProcessBuildService.new(project, @user)
- .execute(build, current_status)
- end
- end
+ created_processables_in_stage_without_needs(index).select do |build|
+ process_build(build, current_status)
+ end
+ end
+
+ def process_builds_with_needs(trigger_build_ids)
+ return false unless trigger_build_ids.present?
+ return false unless Feature.enabled?(:ci_dag_support, project)
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ trigger_build_names = pipeline.statuses
+ .where(id: trigger_build_ids)
+ .select(:name)
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ created_processables
+ .with_needs(trigger_build_names)
+ .find_each
+ .map(&method(:process_build_with_needs))
+ .any?
+ end
+
+ def process_build_with_needs(build)
+ current_status = status_for_build_needs(build.needs.map(&:name))
+
+ return unless HasStatus::COMPLETED_STATUSES.include?(current_status)
+
+ process_build(build, current_status)
+ end
+
+ def process_build(build, current_status)
+ Gitlab::OptimisticLocking.retry_lock(build) do |subject|
+ Ci::ProcessBuildService.new(project, @user)
+ .execute(subject, current_status)
end
end
@@ -43,17 +77,33 @@ module Ci
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
- def stage_indexes_of_created_processables
- created_processables.order(:stage_idx).pluck(Arel.sql('DISTINCT stage_idx'))
+ def status_for_build_needs(needs)
+ pipeline.builds.where(name: needs).latest.status || 'success'
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
- def created_processables_in_stage(index)
- created_processables.where(stage_idx: index)
+ def stage_indexes_of_created_processables_without_needs
+ created_processables_without_needs.order(:stage_idx)
+ .pluck(Arel.sql('DISTINCT stage_idx'))
end
# rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
+ def created_processables_in_stage_without_needs(index)
+ created_processables_without_needs
+ .where(stage_idx: index)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def created_processables_without_needs
+ if Feature.enabled?(:ci_dag_support, project)
+ pipeline.processables.created.without_needs
+ else
+ pipeline.processables.created
+ end
+ end
+
def created_processables
pipeline.processables.created
end
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index fab8a179843..338495ba030 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -5,7 +5,7 @@ module Ci
CLONE_ACCESSORS = %i[pipeline project ref tag options name
allow_failure stage stage_id stage_idx trigger_request
yaml_variables when environment coverage_regex
- description tag_list protected].freeze
+ description tag_list protected needs].freeze
def execute(build)
reprocess!(build).tap do |new_build|
diff --git a/app/services/clusters/refresh_service.rb b/app/services/clusters/refresh_service.rb
deleted file mode 100644
index 3752a306793..00000000000
--- a/app/services/clusters/refresh_service.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- class RefreshService
- def self.create_or_update_namespaces_for_cluster(cluster)
- projects_with_missing_kubernetes_namespaces_for_cluster(cluster).each do |project|
- create_or_update_namespace(cluster, project)
- end
- end
-
- def self.create_or_update_namespaces_for_project(project)
- clusters_with_missing_kubernetes_namespaces_for_project(project).each do |cluster|
- create_or_update_namespace(cluster, project)
- end
- end
-
- def self.projects_with_missing_kubernetes_namespaces_for_cluster(cluster)
- cluster.all_projects.missing_kubernetes_namespace(cluster.kubernetes_namespaces)
- end
-
- private_class_method :projects_with_missing_kubernetes_namespaces_for_cluster
-
- def self.clusters_with_missing_kubernetes_namespaces_for_project(project)
- project.clusters.managed.missing_kubernetes_namespace(project.kubernetes_namespaces)
- end
-
- private_class_method :clusters_with_missing_kubernetes_namespaces_for_project
-
- def self.create_or_update_namespace(cluster, project)
- kubernetes_namespace = cluster.find_or_initialize_kubernetes_namespace_for_project(project)
-
- ::Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
- cluster: cluster,
- kubernetes_namespace: kubernetes_namespace
- ).execute
- end
-
- private_class_method :create_or_update_namespace
- end
-end
diff --git a/app/services/cohorts_service.rb b/app/services/cohorts_service.rb
index 6d466c2fc9c..97fbb70f350 100644
--- a/app/services/cohorts_service.rb
+++ b/app/services/cohorts_service.rb
@@ -95,10 +95,6 @@ class CohortsService
# rubocop: enable CodeReuse/ActiveRecord
def column_to_date(column)
- if Gitlab::Database.postgresql?
- "CAST(DATE_TRUNC('month', #{column}) AS date)"
- else
- "STR_TO_DATE(DATE_FORMAT(#{column}, '%Y-%m-01'), '%Y-%m-%d')"
- end
+ "CAST(DATE_TRUNC('month', #{column}) AS date)"
end
end
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index 01bd685712b..a51ac9aa593 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -18,10 +18,6 @@ module Groups
return namespace
end
- if group_path.include?('/') && !Group.supports_nested_objects?
- raise 'Nested groups are not supported on MySQL'
- end
-
create_group_path
end
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index 98e7c311572..fe7e07ef9f0 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -43,7 +43,6 @@ module Groups
def ensure_allowed_transfer
raise_transfer_error(:group_is_already_root) if group_is_already_root?
- raise_transfer_error(:database_not_supported) unless Group.supports_nested_objects?
raise_transfer_error(:same_parent_as_current) if same_parent?
raise_transfer_error(:invalid_policies) unless valid_policies?
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index db673cace81..77c2224ee3b 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -358,6 +358,7 @@ class IssuableBaseService < BaseService
assignees: issuable.assignees.to_a
}
associations[:total_time_spent] = issuable.total_time_spent if issuable.respond_to?(:total_time_spent)
+ associations[:description] = issuable.description
associations
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 7cd825aa967..c8f4412c9f2 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -61,6 +61,8 @@ module Issues
if added_mentions.present?
notification_service.async.new_mentions_in_issue(issue, added_mentions, current_user)
end
+
+ ZoomNotesService.new(issue, project, current_user, old_description: old_associations[:description]).execute
end
def handle_task_changes(issuable)
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index c8d5e563cd8..0164760920f 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -31,7 +31,7 @@ module Members
return unless member.is_a?(GroupMember) && member.user && member.group
delete_project_members(member)
- delete_subgroup_members(member) if Group.supports_nested_objects?
+ delete_subgroup_members(member)
end
def delete_project_members(member)
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 109c964e577..b28f80939ae 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -11,15 +11,18 @@ module MergeRequests
# https://gitlab.com/gitlab-org/gitlab-ce/issues/53658
merge_quick_actions_into_params!(merge_request, only: [:target_branch])
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) if params.has_key?(:force_remove_source_branch)
- merge_request.assign_attributes(params)
+ # Assign the projects first so we can use policies for `filter_params`
merge_request.author = current_user
+ merge_request.source_project = find_source_project
+ merge_request.target_project = find_target_project
+
+ filter_params(merge_request)
+ merge_request.assign_attributes(params.to_h.compact)
+
merge_request.compare_commits = []
- merge_request.source_project = find_source_project
- merge_request.target_project = find_target_project
- merge_request.target_branch = find_target_branch
- merge_request.can_be_created = projects_and_branches_valid?
- ensure_milestone_available(merge_request)
+ merge_request.target_branch = find_target_branch
+ merge_request.can_be_created = projects_and_branches_valid?
# compare branches only if branches are valid, otherwise
# compare_branches may raise an error
@@ -50,12 +53,14 @@ module MergeRequests
to: :merge_request
def find_source_project
+ source_project = project_from_params(:source_project)
return source_project if source_project.present? && can?(current_user, :create_merge_request_from, source_project)
project
end
def find_target_project
+ target_project = project_from_params(:target_project)
return target_project if target_project.present? && can?(current_user, :create_merge_request_in, target_project)
target_project = project.default_merge_request_target
@@ -65,6 +70,17 @@ module MergeRequests
project
end
+ def project_from_params(param_name)
+ project_from_params = params.delete(param_name)
+
+ id_param_name = :"#{param_name}_id"
+ if project_from_params.nil? && params[id_param_name]
+ project_from_params = Project.find_by_id(params.delete(id_param_name))
+ end
+
+ project_from_params
+ end
+
def find_target_branch
target_branch || target_project.default_branch
end
diff --git a/app/services/merge_requests/mergeability_check_service.rb b/app/services/merge_requests/mergeability_check_service.rb
index 9fa50c9448f..962e2327b3e 100644
--- a/app/services/merge_requests/mergeability_check_service.rb
+++ b/app/services/merge_requests/mergeability_check_service.rb
@@ -3,6 +3,7 @@
module MergeRequests
class MergeabilityCheckService < ::BaseService
include Gitlab::Utils::StrongMemoize
+ include Gitlab::ExclusiveLeaseHelpers
delegate :project, to: :@merge_request
delegate :repository, to: :project
@@ -21,13 +22,35 @@ module MergeRequests
# where we need the current state of the merge ref in repository, the `recheck`
# argument is required.
#
+ # retry_lease - Concurrent calls wait for at least 10 seconds until the
+ # lease is granted (other process finishes running). Returns an error
+ # ServiceResponse if the lease is not granted during this time.
+ #
# Returns a ServiceResponse indicating merge_status is/became can_be_merged
# and the merge-ref is synced. Success in case of being/becoming mergeable,
# error otherwise.
- def execute(recheck: false)
+ def execute(recheck: false, retry_lease: true)
return ServiceResponse.error(message: 'Invalid argument') unless merge_request
return ServiceResponse.error(message: 'Unsupported operation') if Gitlab::Database.read_only?
+ return check_mergeability(recheck) unless merge_ref_auto_sync_lock_enabled?
+
+ in_write_lock(retry_lease: retry_lease) do |retried|
+ # When multiple calls are waiting for the same lock (retry_lease),
+ # it's possible that when granted, the MR status was already updated for
+ # that object, therefore we reset if there was a lease retry.
+ merge_request.reset if retried
+
+ check_mergeability(recheck)
+ end
+ rescue FailedToObtainLockError => error
+ ServiceResponse.error(message: error.message)
+ end
+
+ private
+
+ attr_reader :merge_request
+ def check_mergeability(recheck)
recheck! if recheck
update_merge_status
@@ -46,9 +69,21 @@ module MergeRequests
ServiceResponse.success(payload: payload)
end
- private
+ # It's possible for this service to send concurrent requests to Gitaly in order
+ # to "git update-ref" the same ref. Therefore we handle a light exclusive
+ # lease here.
+ #
+ def in_write_lock(retry_lease:, &block)
+ lease_key = "mergeability_check:#{merge_request.id}"
- attr_reader :merge_request
+ lease_opts = {
+ ttl: 1.minute,
+ retries: retry_lease ? 10 : 0,
+ sleep_sec: retry_lease ? 1.second : 0
+ }
+
+ in_lock(lease_key, lease_opts, &block)
+ end
def payload
strong_memoize(:payload) do
@@ -116,5 +151,9 @@ module MergeRequests
def merge_ref_auto_sync_enabled?
Feature.enabled?(:merge_ref_auto_sync, project, default_enabled: true)
end
+
+ def merge_ref_auto_sync_lock_enabled?
+ Feature.enabled?(:merge_ref_auto_sync_lock, project, default_enabled: true)
+ end
end
end
diff --git a/app/services/metrics/dashboard/base_service.rb b/app/services/metrics/dashboard/base_service.rb
new file mode 100644
index 00000000000..b331bf51874
--- /dev/null
+++ b/app/services/metrics/dashboard/base_service.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+# Searches a projects repository for a metrics dashboard and formats the output.
+# Expects any custom dashboards will be located in `.gitlab/dashboards`
+module Metrics
+ module Dashboard
+ class BaseService < ::BaseService
+ PROCESSING_ERROR = Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardProcessingError
+ NOT_FOUND_ERROR = Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
+
+ def get_dashboard
+ return error('Insufficient permissions.', :unauthorized) unless allowed?
+
+ success(dashboard: process_dashboard)
+ rescue NOT_FOUND_ERROR
+ error("#{dashboard_path} could not be found.", :not_found)
+ rescue PROCESSING_ERROR => e
+ error(e.message, :unprocessable_entity)
+ end
+
+ # Summary of all known dashboards for the service.
+ # @return [Array<Hash>] ex) [{ path: String, default: Boolean }]
+ def self.all_dashboard_paths(_project)
+ raise NotImplementedError
+ end
+
+ # Returns an un-processed dashboard from the cache.
+ def raw_dashboard
+ Gitlab::Metrics::Dashboard::Cache.fetch(cache_key) { get_raw_dashboard }
+ end
+
+ private
+
+ # Determines whether users should be able to view
+ # dashboards at all.
+ def allowed?
+ Ability.allowed?(current_user, :read_environment, project)
+ end
+
+ # Returns a new dashboard Hash, supplemented with DB info
+ def process_dashboard
+ Gitlab::Metrics::Dashboard::Processor
+ .new(project, params[:environment], raw_dashboard)
+ .process(insert_project_metrics: insert_project_metrics?)
+ end
+
+ # @return [String] Relative filepath of the dashboard yml
+ def dashboard_path
+ params[:dashboard_path]
+ end
+
+ # @return [Hash] an unmodified dashboard
+ def get_raw_dashboard
+ raise NotImplementedError
+ end
+
+ # @return [String]
+ def cache_key
+ raise NotImplementedError
+ end
+
+ # Determines whether custom metrics should be included
+ # in the processed output.
+ # @return [Boolean]
+ def insert_project_metrics?
+ false
+ end
+ end
+ end
+end
diff --git a/app/services/metrics/dashboard/default_embed_service.rb b/app/services/metrics/dashboard/default_embed_service.rb
new file mode 100644
index 00000000000..8b01b44fc98
--- /dev/null
+++ b/app/services/metrics/dashboard/default_embed_service.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+# Responsible for returning a filtered system dashboard
+# containing only the default embedded metrics. In future,
+# this class may be updated to support filtering to
+# alternate metrics/panels.
+#
+# Why isn't this filtering in a processing stage? By filtering
+# here, we ensure the dynamically-determined dashboard is cached.
+#
+# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
+module Metrics
+ module Dashboard
+ class DefaultEmbedService < ::Metrics::Dashboard::BaseService
+ # For the default filtering for embedded metrics,
+ # uses the 'id' key in dashboard-yml definition for
+ # identification.
+ DEFAULT_EMBEDDED_METRICS_IDENTIFIERS = %w(
+ system_metrics_kubernetes_container_memory_total
+ system_metrics_kubernetes_container_cores_total
+ ).freeze
+
+ # Returns a new dashboard with only the matching
+ # metrics from the system dashboard, stripped of groups.
+ # @return [Hash]
+ def get_raw_dashboard
+ panels = panel_groups.each_with_object([]) do |group, panels|
+ matched_panels = group['panels'].select { |panel| matching_panel?(panel) }
+
+ panels.concat(matched_panels)
+ end
+
+ { 'panel_groups' => [{ 'panels' => panels }] }
+ end
+
+ def cache_key
+ "dynamic_metrics_dashboard_#{metric_identifiers.join('_')}"
+ end
+
+ private
+
+ # Returns an array of the panels groups on the
+ # system dashboard
+ def panel_groups
+ ::Metrics::Dashboard::SystemDashboardService
+ .new(project, nil)
+ .raw_dashboard['panel_groups']
+ end
+
+ # Identifies a panel as "matching" if any metric ids in
+ # the panel is in the list of identifiers to collect.
+ def matching_panel?(panel)
+ panel['metrics'].any? do |metric|
+ metric_identifiers.include?(metric['id'])
+ end
+ end
+
+ def metric_identifiers
+ DEFAULT_EMBEDDED_METRICS_IDENTIFIERS
+ end
+ end
+ end
+end
diff --git a/app/services/metrics/dashboard/project_dashboard_service.rb b/app/services/metrics/dashboard/project_dashboard_service.rb
new file mode 100644
index 00000000000..756d387c0e6
--- /dev/null
+++ b/app/services/metrics/dashboard/project_dashboard_service.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+# Searches a projects repository for a metrics dashboard and formats the output.
+# Expects any custom dashboards will be located in `.gitlab/dashboards`
+# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
+module Metrics
+ module Dashboard
+ class ProjectDashboardService < ::Metrics::Dashboard::BaseService
+ DASHBOARD_ROOT = ".gitlab/dashboards"
+
+ class << self
+ def all_dashboard_paths(project)
+ file_finder(project)
+ .list_files_for(DASHBOARD_ROOT)
+ .map do |filepath|
+ {
+ path: filepath,
+ display_name: name_for_path(filepath),
+ default: false
+ }
+ end
+ end
+
+ def file_finder(project)
+ Gitlab::Template::Finders::RepoTemplateFinder.new(project, DASHBOARD_ROOT, '.yml')
+ end
+
+ # Grabs the filepath after the base directory.
+ def name_for_path(filepath)
+ filepath.delete_prefix("#{DASHBOARD_ROOT}/")
+ end
+ end
+
+ private
+
+ # Searches the project repo for a custom-defined dashboard.
+ def get_raw_dashboard
+ yml = self.class.file_finder(project).read(dashboard_path)
+
+ YAML.safe_load(yml)
+ end
+
+ def cache_key
+ "project_#{project.id}_metrics_dashboard_#{dashboard_path}"
+ end
+ end
+ end
+end
diff --git a/app/services/metrics/dashboard/system_dashboard_service.rb b/app/services/metrics/dashboard/system_dashboard_service.rb
new file mode 100644
index 00000000000..fcd71aadb03
--- /dev/null
+++ b/app/services/metrics/dashboard/system_dashboard_service.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+# Fetches the system metrics dashboard and formats the output.
+# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
+module Metrics
+ module Dashboard
+ class SystemDashboardService < ::Metrics::Dashboard::BaseService
+ SYSTEM_DASHBOARD_PATH = 'config/prometheus/common_metrics.yml'
+ SYSTEM_DASHBOARD_NAME = 'Default'
+
+ class << self
+ def all_dashboard_paths(_project)
+ [{
+ path: SYSTEM_DASHBOARD_PATH,
+ display_name: SYSTEM_DASHBOARD_NAME,
+ default: true
+ }]
+ end
+
+ def system_dashboard?(filepath)
+ filepath == SYSTEM_DASHBOARD_PATH
+ end
+ end
+
+ private
+
+ def dashboard_path
+ SYSTEM_DASHBOARD_PATH
+ end
+
+ # Returns the base metrics shipped with every GitLab service.
+ def get_raw_dashboard
+ yml = File.read(Rails.root.join(dashboard_path))
+
+ YAML.safe_load(yml)
+ end
+
+ def cache_key
+ "metrics_dashboard_#{dashboard_path}"
+ end
+
+ def insert_project_metrics?
+ true
+ end
+ end
+ end
+end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 1b46f6d8a72..194c4a43dbc 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -21,7 +21,7 @@ module Notes
if quick_actions_service.supported?(note)
options = { merge_request_diff_head_sha: merge_request_diff_head_sha }
- content, update_params = quick_actions_service.execute(note, options)
+ content, update_params, message = quick_actions_service.execute(note, options)
only_commands = content.empty?
@@ -52,7 +52,7 @@ module Notes
# We must add the error after we call #save because errors are reset
# when #save is called
if only_commands
- note.errors.add(:commands_only, 'Commands applied')
+ note.errors.add(:commands_only, message.presence || _('Commands did not apply'))
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index a55771ed538..21fab22e0d4 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -595,7 +595,7 @@ class NotificationService
end
def deliver_access_request_email(recipient, member)
- mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
+ mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.id).deliver_later
end
def fallback_to_group_owners_maintainers?(recipients, member)
diff --git a/app/services/projects/base_move_relations_service.rb b/app/services/projects/base_move_relations_service.rb
index 24dec1f3a45..3a159cef58b 100644
--- a/app/services/projects/base_move_relations_service.rb
+++ b/app/services/projects/base_move_relations_service.rb
@@ -10,17 +10,5 @@ module Projects
true
end
-
- private
-
- # rubocop: disable CodeReuse/ActiveRecord
- def prepare_relation(relation, id_param = :id)
- if Gitlab::Database.postgresql?
- relation
- else
- relation.model.where("#{id_param}": relation.pluck(id_param))
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index b805a7f1211..a1279bfb3a3 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -210,11 +210,20 @@ module Projects
end
def flush_caches(project)
- project.repository.before_delete
+ ignore_git_errors(repo_path) { project.repository.before_delete }
- Repository.new(wiki_path, project, disk_path: repo_path).before_delete
+ ignore_git_errors(wiki_path) { Repository.new(wiki_path, project, disk_path: repo_path).before_delete }
Projects::ForksCountService.new(project).delete_cache
end
+
+ # If we get a Gitaly error, the repository may be corrupted. We can
+ # ignore these errors since we're going to trash the repositories
+ # anyway.
+ def ignore_git_errors(disk_path, &block)
+ yield
+ rescue Gitlab::Git::CommandError => e
+ Gitlab::GitLogger.warn(class: self.class.name, project_id: project.id, disk_path: disk_path, message: e.to_s)
+ end
end
end
diff --git a/app/services/projects/fetch_statistics_increment_service.rb b/app/services/projects/fetch_statistics_increment_service.rb
index 8644e6bf313..b150fd2d9f1 100644
--- a/app/services/projects/fetch_statistics_increment_service.rb
+++ b/app/services/projects/fetch_statistics_increment_service.rb
@@ -12,14 +12,9 @@ module Projects
increment_fetch_count_sql = <<~SQL
INSERT INTO #{table_name} (project_id, date, fetch_count)
VALUES (#{project.id}, '#{Date.today}', 1)
+ ON CONFLICT (project_id, date) DO UPDATE SET fetch_count = #{table_name}.fetch_count + 1
SQL
- increment_fetch_count_sql += if Gitlab::Database.postgresql?
- "ON CONFLICT (project_id, date) DO UPDATE SET fetch_count = #{table_name}.fetch_count + 1"
- else
- "ON DUPLICATE KEY UPDATE fetch_count = #{table_name}.fetch_count + 1"
- end
-
ActiveRecord::Base.connection.execute(increment_fetch_count_sql)
end
diff --git a/app/services/projects/move_deploy_keys_projects_service.rb b/app/services/projects/move_deploy_keys_projects_service.rb
index b6a3af8c7b8..01419563538 100644
--- a/app/services/projects/move_deploy_keys_projects_service.rb
+++ b/app/services/projects/move_deploy_keys_projects_service.rb
@@ -16,8 +16,7 @@ module Projects
private
def move_deploy_keys_projects
- prepare_relation(non_existent_deploy_keys_projects)
- .update_all(project_id: @project.id)
+ non_existent_deploy_keys_projects.update_all(project_id: @project.id)
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/services/projects/move_lfs_objects_projects_service.rb b/app/services/projects/move_lfs_objects_projects_service.rb
index 308a54ad06e..10e19014db4 100644
--- a/app/services/projects/move_lfs_objects_projects_service.rb
+++ b/app/services/projects/move_lfs_objects_projects_service.rb
@@ -16,8 +16,7 @@ module Projects
private
def move_lfs_objects_projects
- prepare_relation(non_existent_lfs_objects_projects)
- .update_all(project_id: @project.lfs_storage_project.id)
+ non_existent_lfs_objects_projects.update_all(project_id: @project.lfs_storage_project.id)
end
def remove_remaining_lfs_objects_project
diff --git a/app/services/projects/move_notification_settings_service.rb b/app/services/projects/move_notification_settings_service.rb
index e740c44bd26..65a888fe26b 100644
--- a/app/services/projects/move_notification_settings_service.rb
+++ b/app/services/projects/move_notification_settings_service.rb
@@ -16,8 +16,7 @@ module Projects
private
def move_notification_settings
- prepare_relation(non_existent_notifications)
- .update_all(source_id: @project.id)
+ non_existent_notifications.update_all(source_id: @project.id)
end
# Remove remaining notification settings from source_project
diff --git a/app/services/projects/move_project_authorizations_service.rb b/app/services/projects/move_project_authorizations_service.rb
index 2985ba89014..c95ad60ab5e 100644
--- a/app/services/projects/move_project_authorizations_service.rb
+++ b/app/services/projects/move_project_authorizations_service.rb
@@ -21,8 +21,7 @@ module Projects
private
def move_project_authorizations
- prepare_relation(non_existent_authorization, :user_id)
- .update_all(project_id: @project.id)
+ non_existent_authorization.update_all(project_id: @project.id)
end
def remove_remaining_authorizations
diff --git a/app/services/projects/move_project_group_links_service.rb b/app/services/projects/move_project_group_links_service.rb
index cf4b291c761..d1aa9af2bcb 100644
--- a/app/services/projects/move_project_group_links_service.rb
+++ b/app/services/projects/move_project_group_links_service.rb
@@ -20,8 +20,7 @@ module Projects
private
def move_group_links
- prepare_relation(non_existent_group_links)
- .update_all(project_id: @project.id)
+ non_existent_group_links.update_all(project_id: @project.id)
end
# Remove remaining project group links from source_project
diff --git a/app/services/projects/move_project_members_service.rb b/app/services/projects/move_project_members_service.rb
index faf389241d2..de4e7e5a1e3 100644
--- a/app/services/projects/move_project_members_service.rb
+++ b/app/services/projects/move_project_members_service.rb
@@ -20,7 +20,7 @@ module Projects
private
def move_project_members
- prepare_relation(non_existent_members).update_all(source_id: @project.id)
+ non_existent_members.update_all(source_id: @project.id)
end
def remove_remaining_members
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 8ff73522e5f..7f944e25887 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -31,17 +31,19 @@ module QuickActions
end
# Takes a text and interprets the commands that are extracted from it.
- # Returns the content without commands, and hash of changes to be applied to a record.
+ # Returns the content without commands, a hash of changes to be applied to a record
+ # and a string containing the execution_message to show to the user.
def execute(content, quick_action_target, only: nil)
- return [content, {}] unless current_user.can?(:use_quick_actions)
+ return [content, {}, ''] unless current_user.can?(:use_quick_actions)
@quick_action_target = quick_action_target
@updates = {}
+ @execution_message = {}
content, commands = extractor.extract_commands(content, only: only)
extract_updates(commands)
- [content, @updates]
+ [content, @updates, execution_messages_for(commands)]
end
# Takes a text and interprets the commands that are extracted from it.
@@ -119,8 +121,12 @@ module QuickActions
labels_params.scan(/"([^"]+)"|([^ ]+)/).flatten.compact
end
- def find_label_references(labels_param)
- find_labels(labels_param).map(&:to_reference)
+ def find_label_references(labels_param, format = :id)
+ labels_to_reference(find_labels(labels_param), format)
+ end
+
+ def labels_to_reference(labels, format = :id)
+ labels.map { |l| l.to_reference(format: format) }
end
def find_label_ids(labels_param)
@@ -128,11 +134,24 @@ module QuickActions
end
def explain_commands(commands)
+ map_commands(commands, :explain)
+ end
+
+ def execution_messages_for(commands)
+ map_commands(commands, :execute_message).join(' ')
+ end
+
+ def map_commands(commands, method)
commands.map do |name, arg|
definition = self.class.definition_by_name(name)
next unless definition
- definition.explain(self, arg)
+ case method
+ when :explain
+ definition.explain(self, arg)
+ when :execute_message
+ @execution_message[name.to_sym] || definition.execute_message(self, arg)
+ end
end.compact
end
diff --git a/app/services/self_monitoring/project/create_service.rb b/app/services/self_monitoring/project/create_service.rb
index e5ef8c15456..8ffd22de127 100644
--- a/app/services/self_monitoring/project/create_service.rb
+++ b/app/services/self_monitoring/project/create_service.rb
@@ -14,6 +14,7 @@ module SelfMonitoring
steps :validate_admins,
:create_project,
:add_project_members,
+ :add_to_whitelist,
:add_prometheus_manual_configuration
def initialize
@@ -59,15 +60,29 @@ module SelfMonitoring
end
end
- def add_prometheus_manual_configuration
+ def add_to_whitelist
return success unless prometheus_enabled?
return success unless prometheus_listen_address.present?
- # TODO: Currently, adding the internal prometheus server as a manual configuration
- # is only possible if the setting to allow webhooks and services to connect
- # to local network is on.
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/44496 will add
- # a whitelist that will allow connections to certain ips on the local network.
+ uri = parse_url(internal_prometheus_listen_address_uri)
+ return error(_('Prometheus listen_address is not a valid URI')) unless uri
+
+ result = ApplicationSettings::UpdateService.new(
+ Gitlab::CurrentSettings.current_application_settings,
+ project_owner,
+ outbound_local_requests_whitelist: [uri.normalized_host]
+ ).execute
+
+ if result
+ success
+ else
+ error(_('Could not add prometheus URL to whitelist'))
+ end
+ end
+
+ def add_prometheus_manual_configuration
+ return success unless prometheus_enabled?
+ return success unless prometheus_listen_address.present?
service = project.find_or_initialize_service('prometheus')
@@ -79,6 +94,11 @@ module SelfMonitoring
success
end
+ def parse_url(uri_string)
+ Addressable::URI.parse(uri_string)
+ rescue Addressable::URI::InvalidURIError, TypeError
+ end
+
def prometheus_enabled?
Gitlab.config.prometheus.enable
rescue Settingslogic::MissingSetting
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index e4564bc9b00..e30debbbe75 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -597,6 +597,14 @@ module SystemNoteService
note_text =~ /\A#{cross_reference_note_prefix}/i
end
+ def zoom_link_added(issue, project, author)
+ create_note(NoteSummary.new(issue, project, author, _('a Zoom call was added to this issue'), action: 'pinned_embed'))
+ end
+
+ def zoom_link_removed(issue, project, author)
+ create_note(NoteSummary.new(issue, project, author, _('a Zoom call was removed from this issue'), action: 'pinned_embed'))
+ end
+
private
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 4a26d2be2af..ae67b4f5256 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -102,13 +102,7 @@ module Users
end
def fresh_authorizations
- klass = if Group.supports_nested_objects?
- Gitlab::ProjectAuthorizations::WithNestedGroups
- else
- Gitlab::ProjectAuthorizations::WithoutNestedGroups
- end
-
- klass.new(user).calculate
+ Gitlab::ProjectAuthorizations.new(user).calculate
end
end
end
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 6d675c026bb..8c294218708 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -17,8 +17,10 @@ class WebHookService
@hook = hook
@data = data
@hook_name = hook_name.to_s
- @request_options = { timeout: Gitlab.config.gitlab.webhook_timeout }
- @request_options.merge!(allow_local_requests: true) if @hook.is_a?(SystemHook)
+ @request_options = {
+ timeout: Gitlab.config.gitlab.webhook_timeout,
+ allow_local_requests: hook.allow_local_requests?
+ }
end
def execute
diff --git a/app/services/zoom_notes_service.rb b/app/services/zoom_notes_service.rb
new file mode 100644
index 00000000000..983a7fcacd1
--- /dev/null
+++ b/app/services/zoom_notes_service.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class ZoomNotesService
+ def initialize(issue, project, current_user, old_description: nil)
+ @issue = issue
+ @project = project
+ @current_user = current_user
+ @old_description = old_description
+ end
+
+ def execute
+ return if @issue.description == @old_description
+
+ if zoom_link_added?
+ zoom_link_added_notification
+ elsif zoom_link_removed?
+ zoom_link_removed_notification
+ end
+ end
+
+ private
+
+ def zoom_link_added?
+ has_zoom_link?(@issue.description) && !has_zoom_link?(@old_description)
+ end
+
+ def zoom_link_removed?
+ !has_zoom_link?(@issue.description) && has_zoom_link?(@old_description)
+ end
+
+ def has_zoom_link?(text)
+ Gitlab::ZoomLinkExtractor.new(text).match?
+ end
+
+ def zoom_link_added_notification
+ SystemNoteService.zoom_link_added(@issue, @project, @current_user)
+ end
+
+ def zoom_link_removed_notification
+ SystemNoteService.zoom_link_removed(@issue, @project, @current_user)
+ end
+end