Commit 2f3d9a2c authored by Shinya Maeda's avatar Shinya Maeda

Squashed commit of the following:

commit 8862cc2b083f59c0a59a2175b047c6b999ac38a7
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Thu Mar 1 18:01:55 2018 +0900

    Change the place of definition of object_storage:archive_legacy_trace

commit 51b26252ff5462c604d852bce9953cd381dcf218
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Thu Mar 1 17:49:37 2018 +0900

    Add object_storage queue to sidekiq_queues.ym. and correct queue name in all_queues.yml.

commit 1313ea164cd59992a469983f8579fe4e2a06e2d8
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Thu Mar 1 17:45:14 2018 +0900

    Fix spec failure on mysql because it can not store long traces

commit d1ba3523139ead0ca7a376c962fd1d7725bf1397
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Thu Mar 1 17:38:44 2018 +0900

    Fix static analysys

commit 9f8c8f9c6bb42c5601c4c15b601256e988871909
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 18:48:08 2018 +0900

    Add spec for workers

commit a34a06fbcc1d14a238b3a0b83e631ac714d8acb2
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 18:43:35 2018 +0900

    Add ObjectStorageQueue concern and test

commit 4203125467f12f32982c454ea50483ad9c04ac62
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 18:43:19 2018 +0900

    Fix rake task to use corrrect SQL

commit 0fb390a4467f868f9c9d9f999589aebeff1e7bec
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 18:14:41 2018 +0900

    Raise an error if conditions are not fulfilled in archive method

commit 87177b4d2c73e63dd4795f3375374dd3ae311c00
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 17:29:24 2018 +0900

    Add changelog

commit 267e1a95fc49f9b926925b3bf03e403f6a5a3dd9
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 03:44:13 2018 +0900

    Add soec for achevie! method. Fixed the method

commit d13208bcd48f701df1e016ea11d7e140b08edafd
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 01:24:23 2018 +0900

    Use find_in_batches for rake task

commit 9362c06bd0fea047be7e050937b4cf3c7caebfdb
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Feb 28 01:24:01 2018 +0900

    Add object_storage queue

commit 8b2d135da2803f89505119a9c2a72b2c50b55396
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Mon Feb 26 23:06:04 2018 +0900

    Add rake task. Adopt the latest fix. Drop CreateTraceArtifactService

commit 8705cd63d1e43039b3e3a2f6d6ecf3fdbe95f0ee
Author: Kamil Trzciński <ayufan@ayufan.eu>
Date:   Fri Feb 23 13:08:38 2018 +0100

    Proper fix for artifacts trace migration which is fully safe
parent a2e767d4
module Ci
class CreateTraceArtifactService < BaseService
def execute(job)
return if job.job_artifacts_trace
job.trace.read do |stream|
break unless stream.file?
clone_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |clone_path|
create_job_trace!(job, clone_path)
FileUtils.rm(stream.path)
end
end
end
private
def create_job_trace!(job, path)
File.open(path) do |stream|
job.create_job_artifacts_trace!(
project: job.project,
file_type: :trace,
file: stream)
end
end
def clone_file!(src_path, temp_dir)
FileUtils.mkdir_p(temp_dir)
Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path|
temp_path = File.join(dir_path, "job.log")
FileUtils.copy(src_path, temp_path)
yield(temp_path)
end
end
end
end
......@@ -62,6 +62,8 @@
- repository_check:repository_check_clear
- repository_check:repository_check_single_repository
- object_storage:archive_legacy_trace
- default
- mailers # ActionMailer::DeliveryJob.queue_name
......
class ArchiveLegacyTraceWorker
include ApplicationWorker
include ObjectStorageQueue
def perform(job_id)
Ci::Build.find_by(id: job_id).try do |job|
job.trace.archive!
end
end
end
# Concern for setting Sidekiq settings for the various GitLab ObjectStorage workers.
module ObjectStorageQueue
extend ActiveSupport::Concern
included do
queue_namespace :object_storage
end
end
......@@ -3,8 +3,8 @@ class CreateTraceArtifactWorker
include PipelineQueue
def perform(job_id)
Ci::Build.preload(:project, :user).find_by(id: job_id).try do |job|
Ci::CreateTraceArtifactService.new(job.project, job.user).execute(job)
Ci::Build.find_by(id: job_id).try do |job|
job.trace.archive!
end
end
end
---
title: Add archive feature to trace
merge_request: 17314
author:
type: added
......@@ -69,6 +69,7 @@
- [storage_migrator, 1]
- [pages_domain_verification, 1]
- [plugin, 1]
- [object_storage, 1]
# EE-specific queues
- [ldap_group_sync, 2]
......@@ -84,4 +85,3 @@
- [elastic_commit_indexer, 1]
- [export_csv, 1]
- [object_storage_upload, 1]
- [object_storage, 1]
......@@ -93,8 +93,52 @@ module Gitlab
job.erase_old_trace!
end
def archive!
raise 'Already archived' if trace_artifact
raise 'Job is not finished yet' unless job.complete?
if current_path
File.open(current_path) do |stream|
archive_stream!(stream)
FileUtils.rm(current_path)
end
elsif old_trace
StringIO.new(old_trace, 'rb').tap do |stream|
archive_stream!(stream)
job.erase_old_trace!
end
end
end
private
def archive_stream!(stream)
clone_file!(stream, JobArtifactUploader.workhorse_upload_path) do |clone_path|
create_job_trace!(job, clone_path)
end
end
def clone_file!(src_stream, temp_dir)
FileUtils.mkdir_p(temp_dir)
Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path|
temp_path = File.join(dir_path, "job.log")
FileUtils.touch(temp_path)
size = IO.copy_stream(src_stream, temp_path)
raise 'Not all saved' unless size == src_stream.size
yield(temp_path)
end
end
def create_job_trace!(job, path)
File.open(path) do |stream|
job.create_job_artifacts_trace!(
project: job.project,
file_type: :trace,
file: stream)
end
end
def ensure_path
return current_path if current_path
......
require 'logger'
require 'resolv-replace'
desc "GitLab | Archive legacy traces to trace artifacts"
namespace :gitlab do
namespace :traces do
task archive: :environment do
logger = Logger.new(STDOUT)
logger.info('Archiving legacy traces')
Ci::Build.finished
.where('NOT EXISTS (?)',
Ci::JobArtifact.select(1).trace.where('ci_builds.id = ci_job_artifacts.job_id'))
.order(id: :asc)
.find_in_batches(batch_size: 1000) do |jobs|
job_ids = jobs.map { |job| [job.id] }
ArchiveLegacyTraceWorker.bulk_perform_async(job_ids)
logger.info("Scheduled #{job_ids.count} jobs. From #{job_ids.min} to #{job_ids.max}")
end
end
end
end
......@@ -399,4 +399,140 @@ describe Gitlab::Ci::Trace do
end
end
end
describe '#archive!' do
subject { trace.archive! }
shared_examples 'archive trace file' do
it do
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
build.reload
expect(build.trace.exist?).to be_truthy
expect(build.job_artifacts_trace.file.exists?).to be_truthy
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(File.exist?(src_path)).to be_falsy
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).digest)
end
end
shared_examples 'source trace file stays intact' do |error:|
it do
expect { subject }.to raise_error(error)
build.reload
expect(build.trace.exist?).to be_truthy
expect(build.job_artifacts_trace).to be_nil
expect(File.exist?(src_path)).to be_truthy
end
end
shared_examples 'archive trace in database' do
it do
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
build.reload
expect(build.trace.exist?).to be_truthy
expect(build.job_artifacts_trace.file.exists?).to be_truthy
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(build.old_trace).to be_nil
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).digest)
end
end
shared_examples 'source trace in database stays intact' do |error:|
it do
expect { subject }.to raise_error(error)
build.reload
expect(build.trace.exist?).to be_truthy
expect(build.job_artifacts_trace).to be_nil
expect(build.old_trace).to eq(trace_content)
end
end
context 'when job does not have trace artifact' do
context 'when trace file stored in default path' do
let!(:build) { create(:ci_build, :success, :trace_live) }
let!(:src_path) { trace.read { |s| return s.path } }
let!(:src_checksum) { Digest::SHA256.file(src_path).digest }
it_behaves_like 'archive trace file'
context 'when failed to create clone file' do
before do
allow_any_instance_of(described_class)
.to receive(:clone_file!).and_raise('Not all saved')
end
it_behaves_like 'source trace file stays intact', error: 'Not all saved'
end
context 'when failed to create job artifact record' do
before do
allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false)
allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages)
.and_return(%w[Error Error])
end
it_behaves_like 'source trace file stays intact', error: ActiveRecord::RecordInvalid
end
end
context 'when trace is stored in database' do
let(:build) { create(:ci_build, :success) }
let(:trace_content) { 'Sample trace' }
let!(:src_checksum) { Digest::SHA256.digest(trace_content) }
before do
build.update_column(:trace, trace_content)
end
it_behaves_like 'archive trace in database'
context 'when failed to create clone file' do
before do
allow_any_instance_of(described_class)
.to receive(:clone_file!).and_raise('Not all saved')
end
it_behaves_like 'source trace in database stays intact', error: 'Not all saved'
end
context 'when failed to create job artifact record' do
before do
allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false)
allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages)
.and_return(%w[Error Error])
end
it_behaves_like 'source trace in database stays intact', error: ActiveRecord::RecordInvalid
end
end
end
context 'when job has trace artifact' do
before do
create(:ci_job_artifact, :trace, job: build)
end
it 'does not archive' do
expect_any_instance_of(described_class).not_to receive(:archive_stream!)
expect { subject }.to raise_error('Already archived')
expect(build.job_artifacts_trace.file.exists?).to be_truthy
end
end
context 'when job is not finished yet' do
let!(:build) { create(:ci_build, :running, :trace_live) }
it 'does not archive' do
expect_any_instance_of(described_class).not_to receive(:archive_stream!)
expect { subject }.to raise_error('Job is not finished yet')
expect(build.trace.exist?).to be_truthy
end
end
end
end
require 'spec_helper'
describe Ci::CreateTraceArtifactService do
describe '#execute' do
subject { described_class.new(nil, nil).execute(job) }
context 'when the job does not have trace artifact' do
context 'when the job has a trace file' do
let!(:job) { create(:ci_build, :trace_live) }
let!(:legacy_path) { job.trace.read { |stream| return stream.path } }
let!(:legacy_checksum) { Digest::SHA256.file(legacy_path).hexdigest }
let(:new_path) { job.job_artifacts_trace.file.path }
let(:new_checksum) { Digest::SHA256.file(new_path).hexdigest }
it { expect(File.exist?(legacy_path)).to be_truthy }
it 'creates trace artifact' do
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
expect(File.exist?(legacy_path)).to be_falsy
expect(File.exist?(new_path)).to be_truthy
expect(new_checksum).to eq(legacy_checksum)
expect(job.job_artifacts_trace.file.exists?).to be_truthy
expect(job.job_artifacts_trace.file.filename).to eq('job.log')
end
context 'when failed to create trace artifact record' do
before do
# When ActiveRecord error happens
allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false)
allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages)
.and_return("Error")
subject rescue nil
job.reload
end
it 'keeps legacy trace and removes trace artifact' do
expect(File.exist?(legacy_path)).to be_truthy
expect(job.job_artifacts_trace).to be_nil
end
end
end
context 'when the job does not have a trace file' do
let!(:job) { create(:ci_build) }
it 'does not create trace artifact' do
expect { subject }.not_to change { Ci::JobArtifact.count }
end
end
end
context 'when the job has already had trace artifact' do
let!(:job) { create(:ci_build, :trace_artifact) }
it 'does not create trace artifact' do
expect { subject }.not_to change { Ci::JobArtifact.count }
end
end
end
end
require 'rake_helper'
describe 'gitlab:traces rake tasks' do
before do
Rake.application.rake_require 'tasks/gitlab/traces'
end
shared_examples 'passes the job id to worker' do
it do
expect(ArchiveLegacyTraceWorker).to receive(:bulk_perform_async).with([[job.id]])
run_rake_task('gitlab:traces:archive')
end
end
shared_examples 'does not pass the job id to worker' do
it do
expect(ArchiveLegacyTraceWorker).not_to receive(:bulk_perform_async)
run_rake_task('gitlab:traces:archive')
end
end
context 'when trace file stored in default path' do
let!(:job) { create(:ci_build, :success, :trace_live) }
it_behaves_like 'passes the job id to worker'
end
context 'when trace is stored in database' do
let!(:job) { create(:ci_build, :success) }
before do
job.update_column(:trace, 'trace in db')
end
it_behaves_like 'passes the job id to worker'
end
context 'when job has trace artifact' do
let!(:job) { create(:ci_build, :success) }
before do
create(:ci_job_artifact, :trace, job: job)
end
it_behaves_like 'does not pass the job id to worker'
end
context 'when job is not finished yet' do
let!(:build) { create(:ci_build, :running, :trace_live) }
it_behaves_like 'does not pass the job id to worker'
end
end
require 'spec_helper'
describe ArchiveLegacyTraceWorker do
it { is_expected.to be_a(ObjectStorageQueue) }
it { is_expected.not_to be_a(PipelineQueue) }
describe '#perform' do
subject { described_class.new.perform(job&.id) }
context 'when job is found' do
let(:job) { create(:ci_build) }
it 'executes service' do
expect_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!)
subject
end
end
context 'when job is not found' do
let(:job) { nil }
it 'does not execute service' do
expect_any_instance_of(Gitlab::Ci::Trace).not_to receive(:archive!)
subject
end
end
end
end
require 'spec_helper'
describe ObjectStorageQueue do
let(:worker) do
Class.new do
def self.name
'DummyWorker'
end
include ApplicationWorker
include ObjectStorageQueue
end
end
it 'sets a default object storage queue automatically' do
expect(worker.sidekiq_options['queue'])
.to eq 'object_storage:dummy'
end
end
......@@ -8,8 +8,7 @@ describe CreateTraceArtifactWorker do
let(:job) { create(:ci_build) }
it 'executes service' do
expect_any_instance_of(Ci::CreateTraceArtifactService)
.to receive(:execute).with(job)
expect_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!)
subject
end
......@@ -19,8 +18,7 @@ describe CreateTraceArtifactWorker do
let(:job) { nil }
it 'does not execute service' do
expect_any_instance_of(Ci::CreateTraceArtifactService)
.not_to receive(:execute)
expect_any_instance_of(Gitlab::Ci::Trace).not_to receive(:archive!)
subject
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment