diff options
Diffstat (limited to 'app/models/concerns')
-rw-r--r-- | app/models/concerns/analytics/cycle_analytics/stage_event_model.rb | 78 | ||||
-rw-r--r-- | app/models/concerns/awardable.rb | 18 | ||||
-rw-r--r-- | app/models/concerns/bulk_users_by_email_load.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/chronic_duration_attribute.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/ci/deployable.rb | 39 | ||||
-rw-r--r-- | app/models/concerns/enums/issuable_link.rb | 12 | ||||
-rw-r--r-- | app/models/concerns/import_state/sidekiq_job_tracker.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/integrations/enable_ssl_verification.rb | 21 | ||||
-rw-r--r-- | app/models/concerns/issuable_link.rb | 28 | ||||
-rw-r--r-- | app/models/concerns/noteable.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/protected_ref_access.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/repository_storage_movable.rb | 1 | ||||
-rw-r--r-- | app/models/concerns/reset_on_column_errors.rb | 50 | ||||
-rw-r--r-- | app/models/concerns/reset_on_union_error.rb | 37 | ||||
-rw-r--r-- | app/models/concerns/routable.rb | 74 | ||||
-rw-r--r-- | app/models/concerns/storage/legacy_namespace.rb | 112 | ||||
-rw-r--r-- | app/models/concerns/vulnerability_finding_helpers.rb | 1 |
17 files changed, 200 insertions, 283 deletions
diff --git a/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb b/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb index d268c32c088..1d9cf5729cd 100644 --- a/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb +++ b/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb @@ -16,6 +16,7 @@ module Analytics scope :start_event_timestamp_before, -> (date) { where(arel_table[:start_event_timestamp].lteq(date)) } scope :authored, ->(user) { where(author_id: user) } scope :with_milestone_id, ->(milestone_id) { where(milestone_id: milestone_id) } + scope :without_milestone_id, -> (milestone_id) { where('milestone_id <> ? or milestone_id IS NULL', milestone_id) } scope :end_event_is_not_happened_yet, -> { where(end_event_timestamp: nil) } scope :order_by_end_event, -> (direction) do # ORDER BY end_event_timestamp, merge_request_id/issue_id, start_event_timestamp @@ -57,45 +58,19 @@ module Analytics class_methods do def upsert_data(data) - upsert_values = data.map do |row| - row.values_at( - :stage_event_hash_id, - :issuable_id, - :group_id, - :project_id, - :milestone_id, - :author_id, - :state_id, - :start_event_timestamp, - :end_event_timestamp - ) - end + upsert_values = data.map { |row| row.values_at(*column_list) } value_list = Arel::Nodes::ValuesList.new(upsert_values).to_sql query = <<~SQL INSERT INTO #{quoted_table_name} ( - stage_event_hash_id, - #{connection.quote_column_name(issuable_id_column)}, - group_id, - project_id, - milestone_id, - author_id, - state_id, - start_event_timestamp, - end_event_timestamp + #{insert_column_list.join(",\n")} ) #{value_list} ON CONFLICT(stage_event_hash_id, #{issuable_id_column}) DO UPDATE SET - group_id = excluded.group_id, - project_id = excluded.project_id, - milestone_id = excluded.milestone_id, - author_id = excluded.author_id, - state_id = excluded.state_id, - start_event_timestamp = excluded.start_event_timestamp, - end_event_timestamp = excluded.end_event_timestamp + #{column_updates.join(",\n")} SQL result = connection.execute(query) @@ -113,6 +88,51 @@ module Analytics def arel_order(arel_node, direction) direction.to_sym == :desc ? arel_node.desc : arel_node.asc end + + def select_columns + [ + issuable_model.arel_table[:id], + issuable_model.arel_table[project_column].as('project_id'), + issuable_model.arel_table[:milestone_id], + issuable_model.arel_table[:author_id], + issuable_model.arel_table[:state_id], + Project.arel_table[:parent_id].as('group_id') + ] + end + + def column_list + [ + :stage_event_hash_id, + :issuable_id, + :group_id, + :project_id, + :milestone_id, + :author_id, + :state_id, + :start_event_timestamp, + :end_event_timestamp + ] + end + + def insert_column_list + [ + :stage_event_hash_id, + connection.quote_column_name(issuable_id_column), + :group_id, + :project_id, + :milestone_id, + :author_id, + :state_id, + :start_event_timestamp, + :end_event_timestamp + ] + end + + def column_updates + insert_column_list.map do |column| + "#{column} = excluded.#{column}" + end + end end end end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index e830594af11..22e71c4fa13 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -13,26 +13,26 @@ module Awardable end class_methods do - def awarded(user, name = nil) + def awarded(user, name = nil, base_class_name = base_class.name, awardable_id_column = :id) award_emoji_table = Arel::Table.new('award_emoji') inner_query = award_emoji_table .project('true') .where(award_emoji_table[:user_id].eq(user.id)) - .where(award_emoji_table[:awardable_type].eq(base_class.name)) - .where(award_emoji_table[:awardable_id].eq(self.arel_table[:id])) + .where(award_emoji_table[:awardable_type].eq(base_class_name)) + .where(award_emoji_table[:awardable_id].eq(self.arel_table[awardable_id_column])) inner_query = inner_query.where(award_emoji_table[:name].eq(name)) if name.present? where(inner_query.exists) end - def not_awarded(user, name = nil) + def not_awarded(user, name = nil, base_class_name = base_class.name, awardable_id_column = :id) award_emoji_table = Arel::Table.new('award_emoji') inner_query = award_emoji_table .project('true') .where(award_emoji_table[:user_id].eq(user.id)) - .where(award_emoji_table[:awardable_type].eq(base_class.name)) - .where(award_emoji_table[:awardable_id].eq(self.arel_table[:id])) + .where(award_emoji_table[:awardable_type].eq(base_class_name)) + .where(award_emoji_table[:awardable_id].eq(self.arel_table[awardable_id_column])) inner_query = inner_query.where(award_emoji_table[:name].eq(name)) if name.present? @@ -52,14 +52,14 @@ module Awardable end # Order votes by emoji, optional sort order param `descending` defaults to true - def order_votes(emoji_name, direction) + def order_votes(emoji_name, direction, base_class_name = base_class.name, awardable_id_column = :id) awardable_table = self.arel_table awards_table = AwardEmoji.arel_table join_clause = awardable_table .join(awards_table, Arel::Nodes::OuterJoin) - .on(awards_table[:awardable_id].eq(awardable_table[:id]) - .and(awards_table[:awardable_type].eq(base_class.name).and(awards_table[:name].eq(emoji_name)))) + .on(awards_table[:awardable_id].eq(awardable_table[awardable_id_column]) + .and(awards_table[:awardable_type].eq(base_class_name).and(awards_table[:name].eq(emoji_name)))) .join_sources joins(join_clause).group(awardable_table[:id]).reorder( diff --git a/app/models/concerns/bulk_users_by_email_load.rb b/app/models/concerns/bulk_users_by_email_load.rb index edbd3e21458..55143ead30a 100644 --- a/app/models/concerns/bulk_users_by_email_load.rb +++ b/app/models/concerns/bulk_users_by_email_load.rb @@ -7,7 +7,7 @@ module BulkUsersByEmailLoad def users_by_emails(emails) Gitlab::SafeRequestLoader.execute(resource_key: user_by_email_resource_key, resource_ids: emails) do |emails| # have to consider all emails - even secondary, so use all_emails here - grouped_users_by_email = User.by_any_email(emails).preload(:emails).group_by(&:all_emails) + grouped_users_by_email = User.by_any_email(emails, confirmed: true).preload(:emails).group_by(&:all_emails) grouped_users_by_email.each_with_object({}) do |(found_emails, users), h| found_emails.each { |e| h[e] = users.first if emails.include?(e) } # don't include all emails for an account, only the ones we want diff --git a/app/models/concerns/chronic_duration_attribute.rb b/app/models/concerns/chronic_duration_attribute.rb index 7b7b61fdf06..44b34cf9b2f 100644 --- a/app/models/concerns/chronic_duration_attribute.rb +++ b/app/models/concerns/chronic_duration_attribute.rb @@ -18,7 +18,7 @@ module ChronicDurationAttribute begin new_value = if value.present? - ChronicDuration.parse(value, use_complete_matcher: true).to_i + ChronicDuration.parse(value).to_i else parameters[:default].presence end diff --git a/app/models/concerns/ci/deployable.rb b/app/models/concerns/ci/deployable.rb index d25151f9a34..844c8a1fa7d 100644 --- a/app/models/concerns/ci/deployable.rb +++ b/app/models/concerns/ci/deployable.rb @@ -4,6 +4,7 @@ module Ci module Deployable extend ActiveSupport::Concern + include Gitlab::Utils::StrongMemoize included do prepend_mod_with('Ci::Deployable') # rubocop: disable Cop/InjectEnterpriseEditionModule @@ -17,8 +18,16 @@ module Ci end end + after_transition any => [:failed] do |job| + next unless job.stops_environment? + + job.run_after_commit do + Environments::StopJobFailedWorker.perform_async(id) + end + end + # Synchronize Deployment Status - # Please note that the data integirty is not assured because we can't use + # Please note that the data integrity is not assured because we can't use # a database transaction due to DB decomposition. after_transition do |job, transition| next if transition.loopback? @@ -32,13 +41,12 @@ module Ci end def outdated_deployment? - strong_memoize(:outdated_deployment) do - deployment_job? && - project.ci_forward_deployment_enabled? && - (!project.ci_forward_deployment_rollback_allowed? || incomplete?) && - deployment&.older_than_last_successful_deployment? - end + deployment_job? && + project.ci_forward_deployment_enabled? && + (!project.ci_forward_deployment_rollback_allowed? || incomplete?) && + deployment&.older_than_last_successful_deployment? end + strong_memoize_attr :outdated_deployment? # Virtual deployment status depending on the environment status. def deployment_status @@ -106,10 +114,10 @@ module Ci namespace = options.dig(:environment, :kubernetes, :namespace) - if namespace.present? # rubocop:disable Style/GuardClause - strong_memoize(:expanded_kubernetes_namespace) do - ExpandVariables.expand(namespace, -> { simple_variables }) - end + return unless namespace.present? + + strong_memoize(:expanded_kubernetes_namespace) do + ExpandVariables.expand(namespace, -> { simple_variables }) end end @@ -146,12 +154,11 @@ module Ci end def environment_status - strong_memoize(:environment_status) do - if has_environment_keyword? && merge_request - EnvironmentStatus.new(project, persisted_environment, merge_request, pipeline.sha) - end - end + return unless has_environment_keyword? && merge_request + + EnvironmentStatus.new(project, persisted_environment, merge_request, pipeline.sha) end + strong_memoize_attr :environment_status def on_stop options&.dig(:environment, :on_stop) diff --git a/app/models/concerns/enums/issuable_link.rb b/app/models/concerns/enums/issuable_link.rb new file mode 100644 index 00000000000..ca5728c2600 --- /dev/null +++ b/app/models/concerns/enums/issuable_link.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Enums + module IssuableLink + TYPE_RELATES_TO = 'relates_to' + TYPE_BLOCKS = 'blocks' + + def self.link_types + { TYPE_RELATES_TO => 0, TYPE_BLOCKS => 1 } + end + end +end diff --git a/app/models/concerns/import_state/sidekiq_job_tracker.rb b/app/models/concerns/import_state/sidekiq_job_tracker.rb index b7d0ed0f51b..9c892acb158 100644 --- a/app/models/concerns/import_state/sidekiq_job_tracker.rb +++ b/app/models/concerns/import_state/sidekiq_job_tracker.rb @@ -19,7 +19,7 @@ module ImportState end def self.jid_by(project_id:, status:) - select(:jid).where(status: status).find_by(project_id: project_id) + select(:id, :jid).where(status: status).find_by(project_id: project_id) end end end diff --git a/app/models/concerns/integrations/enable_ssl_verification.rb b/app/models/concerns/integrations/enable_ssl_verification.rb index 9735a9bf5f6..cb20955488a 100644 --- a/app/models/concerns/integrations/enable_ssl_verification.rb +++ b/app/models/concerns/integrations/enable_ssl_verification.rb @@ -5,7 +5,11 @@ module Integrations extend ActiveSupport::Concern prepended do - boolean_accessor :enable_ssl_verification + field :enable_ssl_verification, + type: :checkbox, + title: -> { s_('Integrations|SSL verification') }, + checkbox_label: -> { s_('Integrations|Enable SSL verification') }, + help: -> { s_('Integrations|Clear if using a self-signed certificate.') } end def initialize_properties @@ -17,18 +21,11 @@ module Integrations def fields super.tap do |fields| url_index = fields.index { |field| field[:name].ends_with?('_url') } - insert_index = url_index ? url_index + 1 : -1 + insert_index = url_index || -1 - fields.insert(insert_index, - Field.new( - name: 'enable_ssl_verification', - integration_class: self, - type: :checkbox, - title: s_('Integrations|SSL verification'), - checkbox_label: s_('Integrations|Enable SSL verification'), - help: s_('Integrations|Clear if using a self-signed certificate.') - ) - ) + enable_ssl_verification_index = fields.index { |field| field[:name] == 'enable_ssl_verification' } + + fields.insert(insert_index, fields.delete_at(enable_ssl_verification_index)) end end end diff --git a/app/models/concerns/issuable_link.rb b/app/models/concerns/issuable_link.rb index e884e5acecf..dcd2705185f 100644 --- a/app/models/concerns/issuable_link.rb +++ b/app/models/concerns/issuable_link.rb @@ -9,8 +9,8 @@ module IssuableLink extend ActiveSupport::Concern - TYPE_RELATES_TO = 'relates_to' - TYPE_BLOCKS = 'blocks' ## EE-only. Kept here to be used on link_type enum. + MAX_LINKS_COUNT = 100 + TYPE_RELATES_TO = Enums::IssuableLink::TYPE_RELATES_TO class_methods do def inverse_link_type(type) @@ -38,10 +38,11 @@ module IssuableLink validates :source, uniqueness: { scope: :target_id, message: 'is already related' } validate :check_self_relation validate :check_opposite_relation + validate :validate_max_number_of_links, on: :create scope :for_source_or_target, ->(issuable) { where(source: issuable).or(where(target: issuable)) } - enum link_type: { TYPE_RELATES_TO => 0, TYPE_BLOCKS => 1 } + enum link_type: Enums::IssuableLink.link_types private @@ -60,6 +61,27 @@ module IssuableLink errors.add(:source, "is already related to this #{self.class.issuable_name}") end end + + def validate_max_number_of_links + return unless source && target + + validate_max_number_of_links_for(source, :source) + validate_max_number_of_links_for(target, :target) + end + + def validate_max_number_of_links_for(item, attribute_name) + return unless item.linked_items_count >= MAX_LINKS_COUNT + + errors.add( + attribute_name, + format( + s_('This %{issuable} would exceed the maximum number of linked %{issuables} (%{limit}).'), + issuable: self.class.issuable_name, + issuables: self.class.issuable_name.pluralize, + limit: MAX_LINKS_COUNT + ) + ) + end end end diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb index 06cee46645b..971089edc45 100644 --- a/app/models/concerns/noteable.rb +++ b/app/models/concerns/noteable.rb @@ -12,12 +12,12 @@ module Noteable class_methods do # `Noteable` class names that support replying to individual notes. def replyable_types - %w[Issue MergeRequest] + %w[Issue MergeRequest AbuseReport] end # `Noteable` class names that support resolvable notes. def resolvable_types - %w[Issue MergeRequest DesignManagement::Design] + %w[Issue MergeRequest DesignManagement::Design AbuseReport] end # `Noteable` class names that support creating/forwarding individual notes. diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb index f0bb1cc359b..a5994b538ce 100644 --- a/app/models/concerns/protected_ref_access.rb +++ b/app/models/concerns/protected_ref_access.rb @@ -71,6 +71,8 @@ module ProtectedRefAccess return false if current_user.nil? || no_access? return current_user.admin? if admin_access? + return false if Feature.enabled?(:check_membership_in_protected_ref_access) && !project.member?(current_user) + yield if block_given? user_can_access?(current_user) diff --git a/app/models/concerns/repository_storage_movable.rb b/app/models/concerns/repository_storage_movable.rb index 87ff413f2c1..77edabb9706 100644 --- a/app/models/concerns/repository_storage_movable.rb +++ b/app/models/concerns/repository_storage_movable.rb @@ -49,6 +49,7 @@ module RepositoryStorageMovable begin storage_move.container.set_repository_read_only!(skip_git_transfer_check: true) rescue StandardError => e + storage_move.do_fail! storage_move.add_error(e.message) next false end diff --git a/app/models/concerns/reset_on_column_errors.rb b/app/models/concerns/reset_on_column_errors.rb new file mode 100644 index 00000000000..8ace52ebff5 --- /dev/null +++ b/app/models/concerns/reset_on_column_errors.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module ResetOnColumnErrors + extend ActiveSupport::Concern + + MAX_RESET_PERIOD = 10.minutes + + included do |base| + base.rescue_from ActiveRecord::StatementInvalid, with: :reset_on_union_error + base.rescue_from ActiveModel::UnknownAttributeError, with: :reset_on_unknown_attribute_error + + base.class_attribute :previous_reset_columns_from_error + end + + class_methods do + def do_reset(exception) + class_to_be_reset = base_class + + class_to_be_reset.reset_column_information + Gitlab::ErrorTracking.log_exception(exception, { reset_model_name: class_to_be_reset.name }) + + class_to_be_reset.previous_reset_columns_from_error = Time.current + end + + def reset_on_union_error(exception) + if exception.message.include?("each UNION query must have the same number of columns") && should_reset? + do_reset(exception) + end + + raise + end + + def should_reset? + return false if base_class.previous_reset_columns_from_error? && + base_class.previous_reset_columns_from_error > MAX_RESET_PERIOD.ago + + Feature.enabled?(:reset_column_information_on_statement_invalid, type: :ops) + end + end + + def reset_on_union_error(exception) + self.class.reset_on_union_error(exception) + end + + def reset_on_unknown_attribute_error(exception) + self.class.do_reset(exception) if self.class.should_reset? + + raise + end +end diff --git a/app/models/concerns/reset_on_union_error.rb b/app/models/concerns/reset_on_union_error.rb deleted file mode 100644 index 42e350b0bed..00000000000 --- a/app/models/concerns/reset_on_union_error.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module ResetOnUnionError - extend ActiveSupport::Concern - - MAX_RESET_PERIOD = 10.minutes - - included do |base| - base.rescue_from ActiveRecord::StatementInvalid, with: :reset_on_union_error - - base.class_attribute :previous_reset_columns_from_error - end - - class_methods do - def reset_on_union_error(exception) - if reset_on_statement_invalid?(exception) - class_to_be_reset = base_class - - class_to_be_reset.reset_column_information - Gitlab::ErrorTracking.log_exception(exception, { reset_model_name: class_to_be_reset.name }) - - class_to_be_reset.previous_reset_columns_from_error = Time.current - end - - raise - end - - def reset_on_statement_invalid?(exception) - return false unless exception.message.include?("each UNION query must have the same number of columns") - - return false if base_class.previous_reset_columns_from_error? && - base_class.previous_reset_columns_from_error > MAX_RESET_PERIOD.ago - - Feature.enabled?(:reset_column_information_on_statement_invalid, type: :ops) - end - end -end diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index ef14ff5fbe2..4c16ba18823 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -15,16 +15,7 @@ module Routable # # Returns a single object, or nil. - # rubocop:disable Metrics/CyclomaticComplexity - # rubocop:disable Metrics/PerceivedComplexity - def self.find_by_full_path( - path, - follow_redirects: false, - route_scope: Route, - redirect_route_scope: RedirectRoute, - optimize_routable: Routable.optimize_routable_enabled? - ) - + def self.find_by_full_path(path, follow_redirects: false, route_scope: nil) return unless path.present? # Convert path to string to prevent DB error: function lower(integer) does not exist @@ -35,49 +26,22 @@ module Routable # # We need to qualify the columns with the table name, to support both direct lookups on # Route/RedirectRoute, and scoped lookups through the Routable classes. - if optimize_routable - path_condition = { path: path } - - source_type_condition = if route_scope == Route - {} - else - { source_type: route_scope.klass.base_class } - end + path_condition = { path: path } - route = - Route.where(source_type_condition).find_by(path_condition) || - Route.where(source_type_condition).iwhere(path_condition).take + source_type_condition = route_scope ? { source_type: route_scope.klass.base_class } : {} - if follow_redirects - route ||= RedirectRoute.where(source_type_condition).iwhere(path_condition).take - end + route = + Route.where(source_type_condition).find_by(path_condition) || + Route.where(source_type_condition).iwhere(path_condition).take - return unless route - return route.source if route_scope == Route - - route_scope.find_by(id: route.source_id) - else - Gitlab::Database.allow_cross_joins_across_databases(url: - "https://gitlab.com/gitlab-org/gitlab/-/issues/420046") do - route = - route_scope.find_by(routes: { path: path }) || - route_scope.iwhere(Route.arel_table[:path] => path).take - - if follow_redirects - route ||= redirect_route_scope.iwhere(RedirectRoute.arel_table[:path] => path).take - end - - next unless route - - route.is_a?(Routable) ? route : route.source - end + if follow_redirects + route ||= RedirectRoute.where(source_type_condition).iwhere(path_condition).take end - end - # rubocop:enable Metrics/PerceivedComplexity - # rubocop:enable Metrics/CyclomaticComplexity - def self.optimize_routable_enabled? - Feature.enabled?(:optimize_routable) + return unless route + return route.source unless route_scope + + route_scope.find_by(id: route.source_id) end included do @@ -107,22 +71,12 @@ module Routable # # Returns a single object, or nil. def find_by_full_path(path, follow_redirects: false) - optimize_routable = Routable.optimize_routable_enabled? - - if optimize_routable - route_scope = all - redirect_route_scope = RedirectRoute - else - route_scope = includes(:route).references(:routes) - redirect_route_scope = joins(:redirect_routes) - end + route_scope = all Routable.find_by_full_path( path, follow_redirects: follow_redirects, - route_scope: route_scope, - redirect_route_scope: redirect_route_scope, - optimize_routable: optimize_routable + route_scope: route_scope ) end diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb deleted file mode 100644 index 5455a2159cd..00000000000 --- a/app/models/concerns/storage/legacy_namespace.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -module Storage - module LegacyNamespace - extend ActiveSupport::Concern - - include Gitlab::ShellAdapter - - def move_dir - proj_with_tags = first_project_with_container_registry_tags - - if proj_with_tags - raise Gitlab::UpdatePathError, "Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry" - end - - parent_was = if saved_change_to_parent? && parent_id_before_last_save.present? - Namespace.find(parent_id_before_last_save) # raise NotFound early if needed - end - - if saved_change_to_parent? - former_parent_full_path = parent_was&.full_path - parent_full_path = parent&.full_path - Gitlab::UploadsTransfer.new.move_namespace(path, former_parent_full_path, parent_full_path) - else - Gitlab::UploadsTransfer.new.rename_namespace(full_path_before_last_save, full_path) - end - - # If repositories moved successfully we need to - # send update instructions to users. - # However we cannot allow rollback since we moved namespace dir - # So we basically we mute exceptions in next actions - begin - send_update_instructions - write_projects_repository_config - rescue StandardError => e - Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e, - full_path_before_last_save: full_path_before_last_save, - full_path: full_path, - action: 'move_dir') - end - - true # false would cancel later callbacks but not rollback - end - - # Hooks - - # Save the storages before the projects are destroyed to use them on after destroy - def prepare_for_destroy - old_repository_storages - end - - private - - def move_repositories - # Move the namespace directory in all storages used by member projects - repository_storages(legacy_only: true).each do |repository_storage| - # Ensure old directory exists before moving it - Gitlab::GitalyClient::NamespaceService.allow do - gitlab_shell.add_namespace(repository_storage, full_path_before_last_save) - - # Ensure new directory exists before moving it (if there's a parent) - gitlab_shell.add_namespace(repository_storage, parent.full_path) if parent - - unless gitlab_shell.mv_namespace(repository_storage, full_path_before_last_save, full_path) - - Gitlab::AppLogger.error("Exception moving path #{repository_storage} from #{full_path_before_last_save} to #{full_path}") - - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise Gitlab::UpdatePathError, 'namespace directory cannot be moved' - end - end - end - end - - def old_repository_storages - @old_repository_storage_paths ||= repository_storages(legacy_only: true) - end - - def repository_storages(legacy_only: false) - # We need to get the storage paths for all the projects, even the ones that are - # pending delete. Unscoping also get rids of the default order, which causes - # problems with SELECT DISTINCT. - Project.unscoped do - namespace_projects = all_projects - namespace_projects = namespace_projects.without_storage_feature(:repository) if legacy_only - namespace_projects.pluck(Arel.sql('distinct(repository_storage)')) - end - end - - def rm_dir - # Remove the namespace directory in all storages paths used by member projects - old_repository_storages.each do |repository_storage| - # Move namespace directory into trash. - # We will remove it later async - new_path = "#{full_path}+#{id}+deleted" - - Gitlab::GitalyClient::NamespaceService.allow do - if gitlab_shell.mv_namespace(repository_storage, full_path, new_path) - Gitlab::AppLogger.info %(Namespace directory "#{full_path}" moved to "#{new_path}") - - # Remove namespace directory async with delay so - # GitLab has time to remove all projects first - run_after_commit do - GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage, new_path) - end - end - end - end - end - end -end diff --git a/app/models/concerns/vulnerability_finding_helpers.rb b/app/models/concerns/vulnerability_finding_helpers.rb index e8a50497b20..94d091e8459 100644 --- a/app/models/concerns/vulnerability_finding_helpers.rb +++ b/app/models/concerns/vulnerability_finding_helpers.rb @@ -50,6 +50,7 @@ module VulnerabilityFindingHelpers finding_data = report_finding.to_hash.except( :compare_key, :identifiers, :location, :scanner, :links, :signatures, :flags, :evidence ) + identifiers = report_finding.identifiers.uniq(&:fingerprint).map do |identifier| Vulnerabilities::Identifier.new(identifier.to_hash.merge({ project: project })) end |