Set artifact working directory to be in the destination store to prevent unnecessary I/O

Similar to #33218, build artifacts were being uploaded into a CarrierWave
temporary directory in the Rails root directory before moved to their
final destination, which could cause a copy across filesystems. This
merge request refactors the work in !11866 so that any uploader can
just override `work_dir` to change the default implementation.

Closes #33274
This commit is contained in:
Stan Hu 2017-06-04 22:12:18 -07:00
parent 6ac1caa01a
commit 8a417f5ae8
7 changed files with 77 additions and 31 deletions

View file

@ -23,6 +23,10 @@ class ArtifactUploader < GitlabUploader
File.join(self.class.local_artifacts_store, 'tmp/cache')
end
def work_dir
File.join(self.class.local_artifacts_store, 'tmp/work')
end
private
def default_local_path

View file

@ -41,4 +41,23 @@ class GitlabUploader < CarrierWave::Uploader::Base
def exists?
file.try(:exists?)
end
# Override this if you don't want to save files by default to the Rails.root directory
def work_dir
# Default path set by CarrierWave:
# https://github.com/carrierwaveuploader/carrierwave/blob/v1.0.0/lib/carrierwave/uploader/cache.rb#L182
CarrierWave.tmp_path
end
private
# To prevent files from moving across filesystems, override the default
# implementation:
# http://github.com/carrierwaveuploader/carrierwave/blob/v1.0.0/lib/carrierwave/uploader/cache.rb#L181-L183
def workfile_path(for_file = original_filename)
# To be safe, keep this directory outside of the the cache directory
# because calling CarrierWave.clean_cache_files! will remove any files in
# the cache directory.
File.join(work_dir, @cache_id, version_name.to_s, for_file)
end
end

View file

@ -16,16 +16,4 @@ class LfsObjectUploader < GitlabUploader
def work_dir
File.join(Gitlab.config.lfs.storage_path, 'tmp', 'work')
end
private
# To prevent LFS files from moving across filesystems, override the default
# implementation:
# http://github.com/carrierwaveuploader/carrierwave/blob/v1.0.0/lib/carrierwave/uploader/cache.rb#L181-L183
def workfile_path(for_file = original_filename)
# To be safe, keep this directory outside of the the cache directory
# because calling CarrierWave.clean_cache_files! will remove any files in
# the cache directory.
File.join(work_dir, @cache_id, version_name.to_s, for_file)
end
end

View file

@ -0,0 +1,4 @@
---
title: Set artifact working directory to be in the destination store to prevent unnecessary I/O
merge_request:
author:

View file

@ -17,22 +17,29 @@ describe ArtifactUploader do
describe '.artifacts_upload_path' do
subject { described_class.artifacts_upload_path }
it { is_expected.to start_with(path) }
it { is_expected.to end_with('tmp/uploads/') }
end
describe '#store_dir' do
subject { uploader.store_dir }
it { is_expected.to start_with(path) }
it { is_expected.to end_with("#{job.project_id}/#{job.id}") }
end
describe '#cache_dir' do
subject { uploader.cache_dir }
it { is_expected.to start_with(path) }
it { is_expected.to end_with('tmp/cache') }
it { is_expected.to end_with('/tmp/cache') }
end
describe '#work_dir' do
subject { uploader.work_dir }
it { is_expected.to start_with(path) }
it { is_expected.to end_with('/tmp/work') }
end
end

View file

@ -53,4 +53,19 @@ describe GitlabUploader do
expect(subject.move_to_store).to eq(true)
end
end
describe '#cache!' do
it 'moves the file from the working directory to the cache directory' do
# One to get the work dir, the other to remove it
expect(subject).to receive(:workfile_path).exactly(2).times.and_call_original
# Test https://github.com/carrierwavesubject/carrierwave/blob/v1.0.0/lib/carrierwave/sanitized_file.rb#L200
expect(FileUtils).to receive(:mv).with(anything, /^#{subject.work_dir}/).and_call_original
expect(FileUtils).to receive(:mv).with(/^#{subject.work_dir}/, /#{subject.cache_dir}/).and_call_original
fixture = Rails.root.join('spec', 'fixtures', 'rails_sample.jpg')
subject.cache!(fixture_file_upload(fixture))
expect(subject.file.path).to match(/#{subject.cache_dir}/)
end
end
end

View file

@ -1,21 +1,9 @@
require 'spec_helper'
describe LfsObjectUploader do
let(:uploader) { described_class.new(build_stubbed(:empty_project)) }
describe '#cache!' do
it 'caches the file in the cache directory' do
# One to get the work dir, the other to remove it
expect(uploader).to receive(:workfile_path).exactly(2).times.and_call_original
expect(FileUtils).to receive(:mv).with(anything, /^#{uploader.work_dir}/).and_call_original
expect(FileUtils).to receive(:mv).with(/^#{uploader.work_dir}/, /^#{uploader.cache_dir}/).and_call_original
fixture = Rails.root.join('spec', 'fixtures', 'rails_sample.jpg')
uploader.cache!(fixture_file_upload(fixture))
expect(uploader.file.path).to start_with(uploader.cache_dir)
end
end
let(:lfs_object) { create(:lfs_object, :with_file) }
let(:uploader) { described_class.new(lfs_object) }
let(:path) { Gitlab.config.lfs.storage_path }
describe '#move_to_cache' do
it 'is true' do
@ -28,4 +16,25 @@ describe LfsObjectUploader do
expect(uploader.move_to_store).to eq(true)
end
end
describe '#store_dir' do
subject { uploader.store_dir }
it { is_expected.to start_with(path) }
it { is_expected.to end_with("#{lfs_object.oid[0, 2]}/#{lfs_object.oid[2, 2]}") }
end
describe '#cache_dir' do
subject { uploader.cache_dir }
it { is_expected.to start_with(path) }
it { is_expected.to end_with('/tmp/cache') }
end
describe '#work_dir' do
subject { uploader.work_dir }
it { is_expected.to start_with(path) }
it { is_expected.to end_with('/tmp/work') }
end
end