Welcome to mirror list, hosted at ThFree Co, Russian Federation.

bulk_delete_runners_service.rb « runners « ci « services « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b6b07746e6190c3a3b8721db360cdbbdc53a5e82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# frozen_string_literal: true

module Ci
  module Runners
    class BulkDeleteRunnersService
      attr_reader :runners

      RUNNER_LIMIT = 50

      # @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 delete_runners
        end

        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: 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

        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
end