diff options
Diffstat (limited to 'app/services/ci/runners/bulk_delete_runners_service.rb')
-rw-r--r-- | app/services/ci/runners/bulk_delete_runners_service.rb | 54 |
1 files changed, 47 insertions, 7 deletions
diff --git a/app/services/ci/runners/bulk_delete_runners_service.rb b/app/services/ci/runners/bulk_delete_runners_service.rb index ce07aa541c2..b6b07746e61 100644 --- a/app/services/ci/runners/bulk_delete_runners_service.rb +++ b/app/services/ci/runners/bulk_delete_runners_service.rb @@ -7,29 +7,69 @@ module Ci RUNNER_LIMIT = 50 - # @param runners [Array<Ci::Runner, Integer>] the runners to unregister/destroy - def initialize(runners:) + # @param runners [Array<Ci::Runner>] the runners to unregister/destroy + # @param current_user [User] the user performing the operation + def initialize(runners:, current_user:) @runners = runners + @current_user = current_user end def execute if @runners # Delete a few runners immediately - return ServiceResponse.success(payload: delete_runners) + return delete_runners end - ServiceResponse.success(payload: { deleted_count: 0, deleted_ids: [] }) + ServiceResponse.success(payload: { deleted_count: 0, deleted_ids: [], errors: [] }) end private def delete_runners + runner_count = @runners.limit(RUNNER_LIMIT + 1).count + authorized_runners_ids, unauthorized_runners_ids = compute_authorized_runners # rubocop:disable CodeReuse/ActiveRecord - runners_to_be_deleted = Ci::Runner.where(id: @runners).limit(RUNNER_LIMIT) + runners_to_be_deleted = + Ci::Runner + .where(id: authorized_runners_ids) + .preload([:taggings, :runner_namespaces, :runner_projects]) # rubocop:enable CodeReuse/ActiveRecord - deleted_ids = runners_to_be_deleted.destroy_all.map(&:id) # rubocop: disable Cop/DestroyAll + deleted_ids = runners_to_be_deleted.destroy_all.map(&:id) # rubocop:disable Cop/DestroyAll - { deleted_count: deleted_ids.count, deleted_ids: deleted_ids } + ServiceResponse.success( + payload: { + deleted_count: deleted_ids.count, + deleted_ids: deleted_ids, + errors: error_messages(runner_count, authorized_runners_ids, unauthorized_runners_ids) + }) + end + + def compute_authorized_runners + # rubocop:disable CodeReuse/ActiveRecord + @current_user.ci_owned_runners.load # preload the owned runners to avoid an N+1 + authorized_runners, unauthorized_runners = + @runners.limit(RUNNER_LIMIT) + .partition { |runner| Ability.allowed?(@current_user, :delete_runner, runner) } + # rubocop:enable CodeReuse/ActiveRecord + + [authorized_runners.map(&:id), unauthorized_runners.map(&:id)] + end + + def error_messages(runner_count, authorized_runners_ids, unauthorized_runners_ids) + errors = [] + + if runner_count > RUNNER_LIMIT + errors << "Can only delete up to #{RUNNER_LIMIT} runners per call. Ignored the remaining runner(s)." + end + + if authorized_runners_ids.empty? + errors << "User does not have permission to delete any of the runners" + elsif unauthorized_runners_ids.any? + failed_ids = unauthorized_runners_ids.map { |runner_id| "##{runner_id}" }.join(', ') + errors << "User does not have permission to delete runner(s) #{failed_ids}" + end + + errors end end end |