1# frozen_string_literal: true
2
3module Gitlab
4  module Cleanup
5    class OrphanJobArtifactFilesBatch
6      BatchFull = Class.new(StandardError)
7
8      class ArtifactFile
9        attr_accessor :path
10
11        def initialize(path)
12          @path = path
13        end
14
15        def artifact_id
16          path.split('/').last.to_i
17        end
18      end
19
20      include Gitlab::Utils::StrongMemoize
21
22      attr_reader :batch_size, :dry_run
23      attr_accessor :artifact_files
24
25      def initialize(batch_size:, dry_run: true, logger: Gitlab::AppLogger)
26        @batch_size = batch_size
27        @dry_run = dry_run
28        @logger = logger
29        @artifact_files = []
30      end
31
32      def clean!
33        return if artifact_files.empty?
34
35        lost_and_found.each do |artifact|
36          clean_one!(artifact)
37        end
38      end
39
40      def full?
41        artifact_files.count >= batch_size
42      end
43
44      def <<(artifact_path)
45        raise BatchFull, "Batch full! Already contains #{artifact_files.count} artifacts" if full?
46
47        artifact_files << ArtifactFile.new(artifact_path)
48      end
49
50      def lost_and_found
51        strong_memoize(:lost_and_found) do
52          artifact_file_ids = artifact_files.map(&:artifact_id)
53          existing_artifact_ids = ::Ci::JobArtifact.id_in(artifact_file_ids).pluck_primary_key
54
55          artifact_files.reject { |artifact| existing_artifact_ids.include?(artifact.artifact_id) }
56        end
57      end
58
59      private
60
61      def clean_one!(artifact_file)
62        log_debug("Found orphan job artifact file @ #{artifact_file.path}")
63
64        remove_file!(artifact_file) unless dry_run
65      end
66
67      def remove_file!(artifact_file)
68        FileUtils.rm_rf(artifact_file.path)
69      end
70
71      def log_info(msg, params = {})
72        @logger.info("#{'[DRY RUN]' if dry_run} #{msg}")
73      end
74
75      def log_debug(msg, params = {})
76        @logger.debug(msg)
77      end
78    end
79  end
80end
81
82Gitlab::Cleanup::OrphanJobArtifactFilesBatch.prepend_mod_with('Gitlab::Cleanup::OrphanJobArtifactFilesBatch')
83