summaryrefslogtreecommitdiff
path: root/lib/gitlab/cleanup/remote_uploads.rb
blob: 03298d960a4ecffeb2e8cbc771373d1901167094 (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
# frozen_string_literal: true
module Gitlab
  module Cleanup
    class RemoteUploads
      attr_reader :logger

      BATCH_SIZE = 100

      def initialize(logger: nil)
        @logger = logger || Rails.logger
      end

      def run!(dry_run: false)
        unless configuration.enabled
          logger.warn "Object storage not enabled. Exit".color(:yellow)

          return
        end

        logger.info "Looking for orphaned remote uploads to remove#{'. Dry run' if dry_run}..."

        each_orphan_file do |file|
          info = if dry_run
                   "Can be moved to lost and found: #{file.key}"
                 else
                   new_path = move_to_lost_and_found(file)
                   "Moved to lost and found: #{file.key} -> #{new_path}"
                 end

          logger.info(info)
        end
      end

      private

      # rubocop: disable CodeReuse/ActiveRecord
      def each_orphan_file
        # we want to skip files already moved to lost_and_found directory
        lost_dir_match = "^#{lost_and_found_dir}\/"

        remote_directory.files.each_slice(BATCH_SIZE) do |remote_files|
          remote_files.reject! { |file| file.key.match(/#{lost_dir_match}/) }
          file_paths = remote_files.map(&:key)
          tracked_paths = Upload
            .where(store: ObjectStorage::Store::REMOTE, path: file_paths)
            .pluck(:path)

          remote_files.reject! { |file| tracked_paths.include?(file.key) }
          remote_files.each do |file|
            yield file
          end
        end
      end
      # rubocop: enable CodeReuse/ActiveRecord

      def move_to_lost_and_found(file)
        new_path = "#{lost_and_found_dir}/#{file.key}"

        file.copy(configuration['remote_directory'], new_path)
        file.destroy

        new_path
      end

      def lost_and_found_dir
        'lost_and_found'
      end

      def remote_directory
        connection.directories.new(key: configuration['remote_directory'])
      end

      def connection
        ::Fog::Storage.new(configuration['connection'].symbolize_keys)
      end

      def configuration
        Gitlab.config.uploads.object_store
      end
    end
  end
end