summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Speicher <robert@gitlab.com>2018-07-03 18:43:04 +0000
committerRobert Speicher <robert@gitlab.com>2018-07-03 18:43:04 +0000
commit88b2532984d09de16fb4aaea2eb8b28527125474 (patch)
tree540bedafeaf61accf6f4bcaeca070313b376cbb5
parentcd5789415b6e561564073693243e890e79596ed2 (diff)
parent1181cf336f7def94b7a00c9b631f7ef0b5e74bba (diff)
downloadgitlab-ce-88b2532984d09de16fb4aaea2eb8b28527125474.tar.gz
Merge branch 'sh-fix-move-issue-with-object-storage' into 'master'
Implement file copy for object storage when moving an issue Closes #48505 See merge request gitlab-org/gitlab-ce!20191
-rw-r--r--app/uploaders/file_uploader.rb28
-rw-r--r--changelogs/unreleased/sh-fix-move-issue-with-object-storage.yml5
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb22
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb64
-rw-r--r--spec/uploaders/file_uploader_spec.rb44
5 files changed, 121 insertions, 42 deletions
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 36bc0a4575a..73606eb9f83 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -81,6 +81,13 @@ class FileUploader < GitlabUploader
apply_context!(uploader_context)
end
+ def initialize_copy(from)
+ super
+
+ @secret = self.class.generate_secret
+ @upload = nil # calling record_upload would delete the old upload if set
+ end
+
# enforce the usage of Hashed storage when storing to
# remote store as the FileMover doesn't support OS
def base_dir(store = nil)
@@ -144,6 +151,27 @@ class FileUploader < GitlabUploader
@secret ||= self.class.generate_secret
end
+ # return a new uploader with a file copy on another project
+ def self.copy_to(uploader, to_project)
+ moved = uploader.dup.tap do |u|
+ u.model = to_project
+ end
+
+ moved.copy_file(uploader.file)
+ moved
+ end
+
+ def copy_file(file)
+ to_path = if file_storage?
+ File.join(self.class.root, store_path)
+ else
+ store_path
+ end
+
+ self.file = file.copy_to(to_path)
+ record_upload # after_store is not triggered
+ end
+
private
def apply_context!(uploader_context)
diff --git a/changelogs/unreleased/sh-fix-move-issue-with-object-storage.yml b/changelogs/unreleased/sh-fix-move-issue-with-object-storage.yml
new file mode 100644
index 00000000000..9c5c4ca20d8
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-move-issue-with-object-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Implement upload copy when moving an issue with upload on object storage
+merge_request: 20191
+author:
+type: fixed
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
index b6eeb5d9a2b..f7e66697da3 100644
--- a/lib/gitlab/gfm/uploads_rewriter.rb
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -23,11 +23,8 @@ module Gitlab
file = find_file(@source_project, $~[:secret], $~[:file])
break markdown unless file.try(:exists?)
- new_uploader = FileUploader.new(target_project)
- with_link_in_tmp_dir(file.file) do |open_tmp_file|
- new_uploader.store!(open_tmp_file)
- end
- new_uploader.markdown_link
+ moved = FileUploader.copy_to(file, target_project)
+ moved.markdown_link
end
end
@@ -48,20 +45,7 @@ module Gitlab
def find_file(project, secret, file)
uploader = FileUploader.new(project, secret: secret)
uploader.retrieve_from_store!(file)
- uploader.file
- end
-
- # Because the uploaders use 'move_to_store' we must have a temporary
- # file that is allowed to be (re)moved.
- def with_link_in_tmp_dir(file)
- dir = Dir.mktmpdir('UploadsRewriter', File.dirname(file))
- # The filename matters to Carrierwave so we make sure to preserve it
- tmp_file = File.join(dir, File.basename(file))
- File.link(file, tmp_file)
- # Open the file to placate Carrierwave
- File.open(tmp_file) { |open_file| yield open_file }
- ensure
- FileUtils.rm_rf(dir)
+ uploader
end
end
end
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 13df8531b63..ef52a25f47e 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -20,37 +20,55 @@ describe Gitlab::Gfm::UploadsRewriter do
"Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}"
end
- describe '#rewrite' do
- let!(:new_text) { rewriter.rewrite(new_project) }
+ shared_examples "files are accessible" do
+ describe '#rewrite' do
+ let!(:new_text) { rewriter.rewrite(new_project) }
- let(:old_files) { [image_uploader, zip_uploader].map(&:file) }
- let(:new_files) do
- described_class.new(new_text, new_project, user).files
- end
+ let(:old_files) { [image_uploader, zip_uploader] }
+ let(:new_files) do
+ described_class.new(new_text, new_project, user).files
+ end
- let(:old_paths) { old_files.map(&:path) }
- let(:new_paths) { new_files.map(&:path) }
+ let(:old_paths) { old_files.map(&:path) }
+ let(:new_paths) { new_files.map(&:path) }
- it 'rewrites content' do
- expect(new_text).not_to eq text
- expect(new_text.length).to eq text.length
- end
+ it 'rewrites content' do
+ expect(new_text).not_to eq text
+ expect(new_text.length).to eq text.length
+ end
- it 'copies files' do
- expect(new_files).to all(exist)
- expect(old_paths).not_to match_array new_paths
- expect(old_paths).to all(include(old_project.disk_path))
- expect(new_paths).to all(include(new_project.disk_path))
- end
+ it 'copies files' do
+ expect(new_files).to all(exist)
+ expect(old_paths).not_to match_array new_paths
+ expect(old_paths).to all(include(old_project.disk_path))
+ expect(new_paths).to all(include(new_project.disk_path))
+ end
- it 'does not remove old files' do
- expect(old_files).to all(exist)
+ it 'does not remove old files' do
+ expect(old_files).to all(exist)
+ end
+
+ it 'generates a new secret for each file' do
+ expect(new_paths).not_to include image_uploader.secret
+ expect(new_paths).not_to include zip_uploader.secret
+ end
end
+ end
- it 'generates a new secret for each file' do
- expect(new_paths).not_to include image_uploader.secret
- expect(new_paths).not_to include zip_uploader.secret
+ context "file are stored locally" do
+ include_examples "files are accessible"
+ end
+
+ context "files are stored remotely" do
+ before do
+ stub_uploads_object_storage(FileUploader)
+
+ old_files.each do |file|
+ file.migrate!(ObjectStorage::Store::REMOTE)
+ end
end
+
+ include_examples "files are accessible"
end
describe '#needs_rewrite?' do
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 59013a02938..7ba28b4fc1f 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -80,6 +80,50 @@ describe FileUploader do
end
end
+ describe 'copy_to' do
+ shared_examples 'returns a valid uploader' do
+ describe 'returned uploader' do
+ let(:new_project) { create(:project) }
+ let(:moved) { described_class.copy_to(subject, new_project) }
+
+ it 'generates a new secret' do
+ expect(subject).to be
+ expect(described_class).to receive(:generate_secret).once.and_call_original
+ expect(moved).to be
+ end
+
+ it 'create new upload' do
+ expect(moved.upload).not_to eq(subject.upload)
+ end
+
+ it 'copies the file' do
+ expect(subject.file).to exist
+ expect(moved.file).to exist
+ expect(subject.file).not_to eq(moved.file)
+ expect(subject.object_store).to eq(moved.object_store)
+ end
+ end
+ end
+
+ context 'files are stored locally' do
+ before do
+ subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
+ end
+
+ include_examples 'returns a valid uploader'
+ end
+
+ context 'files are stored remotely' do
+ before do
+ stub_uploads_object_storage
+ subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
+ subject.migrate!(ObjectStorage::Store::REMOTE)
+ end
+
+ include_examples 'returns a valid uploader'
+ end
+ end
+
describe '#secret' do
it 'generates a secret if none is provided' do
expect(described_class).to receive(:generate_secret).and_return('secret')