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

delete_subgroups.rb « tools « qa « qa - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c5d5a25f93346bb308f54c37eb2fb2a6549db81f (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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# frozen_string_literal: true

# This script deletes all subgroups of a group specified by ENV['TOP_LEVEL_GROUP_NAME']
#
# Required environment variables: GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
# Optional environment variable: TOP_LEVEL_GROUP_NAME (defaults to 'gitlab-qa-sandbox-group-<current weekday #>')

# Optional environment variable: CLEANUP_ALL_QA_SANDBOX_GROUPS (defaults to false)
# Set CLEANUP_ALL_QA_SANDBOX_GROUPS to true if you would like to delete all subgroups under all
# 'gitlab-qa-sandbox-group-*' groups. Otherwise, this will fall back to TOP_LEVEL_GROUP_NAME.

# Optional environment variable: PERMANENTLY_DELETE (defaults to false)
# Set PERMANENTLY_DELETE to true if you would like to permanently delete subgroups on an environment with
# deletion protection enabled. Otherwise, subgroups will remain available during the retention period specified
# in admin settings. On environments with deletion protection disabled, subgroups will always be permanently deleted.
#
# Run `rake delete_subgroups`

module QA
  module Tools
    class DeleteSubgroups
      include Support::API
      include Ci::Helpers
      include Lib::Group

      def initialize(delete_before: (Date.today - 3).to_s)
        raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
        raise ArgumentError, "Please provide GITLAB_QA_ACCESS_TOKEN" unless ENV['GITLAB_QA_ACCESS_TOKEN']

        @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'], personal_access_token: ENV['GITLAB_QA_ACCESS_TOKEN'])
        @failed_deletion_attempts = []
        @delete_before = Date.parse(delete_before)
      end

      def run
        if ENV['CLEANUP_ALL_QA_SANDBOX_GROUPS']
          (1..7).each do |group_number|
            group_id = fetch_group_id(@api_client, group_number)
            delete_subgroups(group_id)
          end

        else
          group_id = fetch_group_id(@api_client)
          delete_subgroups(group_id)
        end
      end

      private

      def delete_subgroups(group_id)
        return unless group_id

        subgroups = fetch_subgroups(group_id)
        return logger.info('No subgroups available') if subgroups.empty?

        subgroups_marked_for_deletion = mark_for_deletion(subgroups)

        if ENV['PERMANENTLY_DELETE'] && !subgroups_marked_for_deletion.empty?
          delete_permanently(subgroups_marked_for_deletion)
        end

        print_failed_deletion_attempts

        logger.info('Done')
      end

      def fetch_subgroups(group_id)
        logger.info("Fetching subgroups...")

        api_path = "/groups/#{group_id}/subgroups"
        page_no = '1'
        subgroups = []

        while page_no.present?
          subgroups_response = get Runtime::API::Request.new(@api_client, api_path, page: page_no, per_page: '100').url

          if subgroups_response.code == HTTP_STATUS_OK
            # Do not delete subgroups that are less than 4 days old (for debugging purposes)
            subgroups.concat(parse_body(subgroups_response).select { |subgroup| Date.parse(subgroup[:created_at]) < @delete_before })
          else
            logger.error("Request for subgroups returned (#{subgroups_response.code}): `#{subgroups_response}` ")
          end

          page_no = subgroups_response.headers[:x_next_page].to_s
        end

        subgroups
      end

      def subgroup_request(subgroup, **options)
        Runtime::API::Request.new(@api_client, "/groups/#{subgroup[:id]}", **options).url
      end

      def process_response_and_subgroup(response, subgroup, opts = {})
        if response.code == 202
          logger.info("Success\n")
          opts[:save_successes_to] << subgroup if opts[:save_successes_to]
        else
          logger.error("Failed - #{response}\n")
          @failed_deletion_attempts << { path: subgroup[:full_path], response: response }
        end
      end

      def mark_for_deletion(subgroups)
        subgroups_marked_for_deletion = []

        logger.info("Marking #{subgroups.length} subgroups for deletion...\n")

        subgroups.each do |subgroup|
          path = subgroup[:full_path]

          if subgroup[:marked_for_deletion_on].nil?
            logger.info("Marking subgroup #{path} for deletion...")
            response = delete(subgroup_request(subgroup))

            process_response_and_subgroup(response, subgroup, save_successes_to: subgroups_marked_for_deletion)
          else
            logger.info("Subgroup #{path} already marked for deletion\n")
            subgroups_marked_for_deletion << subgroup
          end
        end

        subgroups_marked_for_deletion
      end

      def subgroup_exists?(subgroup)
        response = get(subgroup_request(subgroup))

        if response.code == 404
          logger.info("Subgroup #{subgroup[:full_path]} is no longer available\n")
          false
        else
          true
        end
      end

      def delete_permanently(subgroups)
        logger.info("Permanently deleting #{subgroups.length} subgroups...\n")

        subgroups.each do |subgroup|
          path = subgroup[:full_path]

          next unless subgroup_exists?(subgroup)

          logger.info("Permanently deleting subgroup #{path}...")
          delete_subgroup_response = delete(subgroup_request(subgroup, permanently_remove: true, full_path: path))

          process_response_and_subgroup(delete_subgroup_response, subgroup)
        end
      end

      def print_failed_deletion_attempts
        if @failed_deletion_attempts.empty?
          logger.info('No failed deletion attempts to report!')
        else
          logger.info("There were #{@failed_deletion_attempts.length} failed deletion attempts:\n")

          @failed_deletion_attempts.each do |attempt|
            logger.info("Subgroup: #{attempt[:path]}")
            logger.error("Response: #{attempt[:response]}\n")
          end
        end
      end
    end
  end
end