summaryrefslogtreecommitdiff
path: root/lib/gitlab/background_migration/legacy_upload_mover.rb
blob: 051c1176edbd898616a3ef821345807ab2c6e061 (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
# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    # This class takes a legacy upload and migrates it to the correct location
    class LegacyUploadMover
      include Gitlab::Utils::StrongMemoize

      attr_reader :upload, :project, :note
      attr_accessor :logger

      def initialize(upload)
        @upload = upload
        @note = Note.find_by(id: upload.model_id)
        @project = note&.project
        @logger = Gitlab::BackgroundMigration::Logger.build
      end

      def execute
        return unless upload

        if !project
          # if we don't have models associated with the upload we can not move it
          warn('Deleting upload due to model not found.')

          destroy_legacy_upload
        elsif note.is_a?(LegacyDiffNote)
          return unless move_legacy_diff_file

          migrate_upload
        elsif !legacy_file_exists?
          warn('Deleting upload due to file not found.')
          destroy_legacy_upload
        else
          migrate_upload
        end
      end

      private

      def migrate_upload
        return unless copy_upload_to_project

        add_upload_link_to_note_text
        destroy_legacy_file
        destroy_legacy_upload
      end

      # we should proceed and log whenever one upload copy fails, no matter the reasons
      # rubocop: disable Lint/RescueException
      def copy_upload_to_project
        @uploader = FileUploader.copy_to(legacy_file_uploader, project)

        logger.info(
          message: 'MigrateLegacyUploads: File copied successfully',
          old_path: legacy_file_uploader.file.path, new_path: @uploader.file.path
        )
        true
      rescue Exception => e
        warn(
          'File could not be copied to project uploads',
          file_path: legacy_file_uploader.file.path, error: e.message
        )
        false
      end
      # rubocop: enable Lint/RescueException

      def destroy_legacy_upload
        if note
          note.remove_attachment = true
          note.save
        end

        if upload.destroy
          logger.info(message: 'MigrateLegacyUploads: Upload was destroyed.', upload: upload.inspect)
        else
          warn('MigrateLegacyUploads: Upload destroy failed.')
        end
      end

      def destroy_legacy_file
        legacy_file_uploader.file.delete
      end

      def add_upload_link_to_note_text
        new_text = "#{note.note} \n #{@uploader.markdown_link}"
        # Bypass validations because old data may have invalid
        # noteable values. If we fail hard here, we may kill the
        # entire background migration, which affects a range of notes.
        note.update_attribute(:note, new_text)
      end

      def legacy_file_uploader
        strong_memoize(:legacy_file_uploader) do
          uploader = upload.build_uploader
          uploader.retrieve_from_store!(File.basename(upload.path))
          uploader
        end
      end

      def legacy_file_exists?
        legacy_file_uploader.file.exists?
      end

      # we should proceed and log whenever one upload copy fails, no matter the reasons
      # rubocop: disable Lint/RescueException
      def move_legacy_diff_file
        old_path = upload.absolute_path
        old_path_sub = '-/system/note/attachment'

        if !File.exist?(old_path) || !old_path.include?(old_path_sub)
          log_legacy_diff_note_problem(old_path)
          return false
        end

        new_path = upload.absolute_path.sub(old_path_sub, '-/system/legacy_diff_note/attachment')
        new_dir = File.dirname(new_path)
        FileUtils.mkdir_p(new_dir)

        FileUtils.mv(old_path, new_path)
      rescue Exception => e
        log_legacy_diff_note_problem(old_path, new_path, e)
        false
      end

      def warn(message, params = {})
        logger.warn(
          params.merge(message: "MigrateLegacyUploads: #{message}", upload: upload.inspect)
        )
      end

      def log_legacy_diff_note_problem(old_path, new_path = nil, error = nil)
        warn('LegacyDiffNote upload could not be moved to a new path',
          old_path: old_path, new_path: new_path, error: error&.message
        )
      end
      # rubocop: enable Lint/RescueException
    end
  end
end