diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-28 03:09:08 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-28 03:09:08 +0300 |
commit | f54a50aa826d0eedcf2e56f51462613bc132f826 (patch) | |
tree | 7194aca23f9af822ea55966a6f477b3d8d68ee47 /app/services | |
parent | c77fda905a8619b756163c10a75171dc9cfe7084 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/services')
-rw-r--r-- | app/services/projects/destroy_service.rb | 19 | ||||
-rw-r--r-- | app/services/repositories/base_service.rb | 8 | ||||
-rw-r--r-- | app/services/repositories/destroy_service.rb | 4 | ||||
-rw-r--r-- | app/services/snippets/bulk_destroy_service.rb | 74 | ||||
-rw-r--r-- | app/services/snippets/destroy_service.rb | 30 | ||||
-rw-r--r-- | app/services/users/destroy_service.rb | 5 |
6 files changed, 122 insertions, 18 deletions
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 066d1f1ca72..fd1366d2c4a 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -47,7 +47,7 @@ module Projects private - def trash_repositories! + def trash_project_repositories! unless remove_repository(project.repository) raise_error(s_('DeleteProject|Failed to remove project repository. Please try again or contact administrator.')) end @@ -57,6 +57,18 @@ module Projects end end + def trash_relation_repositories! + unless remove_snippets + raise_error(s_('DeleteProject|Failed to remove project snippets. Please try again or contact administrator.')) + end + end + + def remove_snippets + response = Snippets::BulkDestroyService.new(current_user, project.snippets).execute + + response.success? + end + def remove_repository(repository) return true unless repository @@ -95,7 +107,8 @@ module Projects Project.transaction do log_destroy_event - trash_repositories! + trash_relation_repositories! + trash_project_repositories! # Rails attempts to load all related records into memory before # destroying: https://github.com/rails/rails/issues/22510 @@ -103,7 +116,7 @@ module Projects # # Exclude container repositories because its before_destroy would be # called multiple times, and it doesn't destroy any database records. - project.destroy_dependent_associations_in_batches(exclude: [:container_repositories]) + project.destroy_dependent_associations_in_batches(exclude: [:container_repositories, :snippets]) project.destroy! end end diff --git a/app/services/repositories/base_service.rb b/app/services/repositories/base_service.rb index 6a39399c791..a99a65b7edb 100644 --- a/app/services/repositories/base_service.rb +++ b/app/services/repositories/base_service.rb @@ -7,8 +7,8 @@ class Repositories::BaseService < BaseService attr_reader :repository - delegate :project, :disk_path, :full_path, to: :repository - delegate :repository_storage, to: :project + delegate :container, :disk_path, :full_path, to: :repository + delegate :repository_storage, to: :container def initialize(repository) @repository = repository @@ -31,7 +31,7 @@ class Repositories::BaseService < BaseService # gitlab/cookies.git -> gitlab/cookies+119+deleted.git # def removal_path - "#{disk_path}+#{project.id}#{DELETED_FLAG}" + "#{disk_path}+#{container.id}#{DELETED_FLAG}" end # If we get a Gitaly error, the repository may be corrupted. We can @@ -40,7 +40,7 @@ class Repositories::BaseService < BaseService def ignore_git_errors(&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) + Gitlab::GitLogger.warn(class: self.class.name, container_id: container.id, disk_path: disk_path, message: e.to_s) end def move_error(path) diff --git a/app/services/repositories/destroy_service.rb b/app/services/repositories/destroy_service.rb index 374968f610e..b12d0744387 100644 --- a/app/services/repositories/destroy_service.rb +++ b/app/services/repositories/destroy_service.rb @@ -14,11 +14,11 @@ class Repositories::DestroyService < Repositories::BaseService log_info(%Q{Repository "#{disk_path}" moved to "#{removal_path}" for repository "#{full_path}"}) current_repository = repository - project.run_after_commit do + container.run_after_commit do Repositories::ShellDestroyService.new(current_repository).execute end - log_info("Project \"#{project.full_path}\" was removed") + log_info("Repository \"#{full_path}\" was removed") success else diff --git a/app/services/snippets/bulk_destroy_service.rb b/app/services/snippets/bulk_destroy_service.rb new file mode 100644 index 00000000000..d9cc383a5a6 --- /dev/null +++ b/app/services/snippets/bulk_destroy_service.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Snippets + class BulkDestroyService + include Gitlab::Allowable + + attr_reader :current_user, :snippets + + DeleteRepositoryError = Class.new(StandardError) + SnippetAccessError = Class.new(StandardError) + + def initialize(user, snippets) + @current_user = user + @snippets = snippets + end + + def execute + return ServiceResponse.success(message: 'No snippets found.') if snippets.empty? + + user_can_delete_snippets! + attempt_delete_repositories! + snippets.destroy_all # rubocop: disable DestroyAll + + ServiceResponse.success(message: 'Snippets were deleted.') + rescue SnippetAccessError + service_response_error("You don't have access to delete these snippets.", 403) + rescue DeleteRepositoryError + attempt_rollback_repositories + service_response_error('Failed to delete snippet repositories.', 400) + rescue + # In case the delete operation fails + attempt_rollback_repositories + service_response_error('Failed to remove snippets.', 400) + end + + private + + def user_can_delete_snippets! + allowed = DeclarativePolicy.user_scope do + snippets.find_each.all? { |snippet| user_can_delete_snippet?(snippet) } + end + + raise SnippetAccessError unless allowed + end + + def user_can_delete_snippet?(snippet) + can?(current_user, :admin_snippet, snippet) + end + + def attempt_delete_repositories! + snippets.each do |snippet| + result = Repositories::DestroyService.new(snippet.repository).execute + + raise DeleteRepositoryError if result[:status] == :error + end + end + + def attempt_rollback_repositories + snippets.each do |snippet| + result = Repositories::DestroyRollbackService.new(snippet.repository).execute + + log_rollback_error(snippet) if result[:status] == :error + end + end + + def log_rollback_error(snippet) + Gitlab::AppLogger.error("Repository #{snippet.full_path} in path #{snippet.disk_path} could not be rolled back") + end + + def service_response_error(message, http_status) + ServiceResponse.error(message: message, http_status: http_status) + end + end +end diff --git a/app/services/snippets/destroy_service.rb b/app/services/snippets/destroy_service.rb index c1e87e74aa4..977626fcf17 100644 --- a/app/services/snippets/destroy_service.rb +++ b/app/services/snippets/destroy_service.rb @@ -4,12 +4,13 @@ module Snippets class DestroyService include Gitlab::Allowable - attr_reader :current_user, :project + attr_reader :current_user, :snippet + + DestroyError = Class.new(StandardError) def initialize(user, snippet) @current_user = user @snippet = snippet - @project = snippet&.project end def execute @@ -24,16 +25,29 @@ module Snippets ) end - if snippet.destroy - ServiceResponse.success(message: 'Snippet was deleted.') - else - service_response_error('Failed to remove snippet.', 400) - end + attempt_destroy! + + ServiceResponse.success(message: 'Snippet was deleted.') + rescue DestroyError + service_response_error('Failed to remove snippet repository.', 400) + rescue + attempt_rollback_repository + service_response_error('Failed to remove snippet.', 400) end private - attr_reader :snippet + def attempt_destroy! + result = Repositories::DestroyService.new(snippet.repository).execute + + raise DestroyError if result[:status] == :error + + snippet.destroy! + end + + def attempt_rollback_repository + Repositories::DestroyRollbackService.new(snippet.repository).execute + end def user_can_delete_snippet? can?(current_user, :admin_snippet, snippet) diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb index ef79ee3d06e..587a8516394 100644 --- a/app/services/users/destroy_service.rb +++ b/app/services/users/destroy_service.rb @@ -56,10 +56,13 @@ module Users MigrateToGhostUserService.new(user).execute unless options[:hard_delete] + response = Snippets::BulkDestroyService.new(current_user, user.snippets).execute + raise DestroyError, response.message if response.error? + # Rails attempts to load all related records into memory before # destroying: https://github.com/rails/rails/issues/22510 # This ensures we delete records in batches. - user.destroy_dependent_associations_in_batches + user.destroy_dependent_associations_in_batches(exclude: [:snippets]) # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing user_data = user.destroy |