Commit 89efaf2a authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'feature/sm/37239-implement-failure_reason-on-ci_builds' into 'master'

Implement `failure_reason` on `ci_builds`

Closes #37239

See merge request !13937
parents 1aa8b38c 38d9b4d7
...@@ -38,6 +38,14 @@ class CommitStatus < ActiveRecord::Base ...@@ -38,6 +38,14 @@ class CommitStatus < ActiveRecord::Base
scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) } scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
scope :after_stage, -> (index) { where('stage_idx > ?', index) } scope :after_stage, -> (index) { where('stage_idx > ?', index) }
enum failure_reason: {
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4
}
state_machine :status do state_machine :status do
event :process do event :process do
transition [:skipped, :manual] => :created transition [:skipped, :manual] => :created
...@@ -79,6 +87,11 @@ class CommitStatus < ActiveRecord::Base ...@@ -79,6 +87,11 @@ class CommitStatus < ActiveRecord::Base
commit_status.finished_at = Time.now commit_status.finished_at = Time.now
end end
before_transition any => :failed do |commit_status, transition|
failure_reason = transition.args.first
commit_status.failure_reason = failure_reason
end
after_transition do |commit_status, transition| after_transition do |commit_status, transition|
next if transition.loopback? next if transition.loopback?
......
...@@ -53,7 +53,7 @@ module Projects ...@@ -53,7 +53,7 @@ module Projects
log_error("Projects::UpdatePagesService: #{message}") log_error("Projects::UpdatePagesService: #{message}")
@status.allow_failure = !latest? @status.allow_failure = !latest?
@status.description = message @status.description = message
@status.drop @status.drop(:script_failure)
super super
end end
......
...@@ -53,7 +53,7 @@ class StuckCiJobsWorker ...@@ -53,7 +53,7 @@ class StuckCiJobsWorker
def drop_build(type, build, status, timeout) def drop_build(type, build, status, timeout)
Rails.logger.info "#{self.class}: Dropping #{type} build #{build.id} for runner #{build.runner_id} (status: #{status}, timeout: #{timeout})" Rails.logger.info "#{self.class}: Dropping #{type} build #{build.id} for runner #{build.runner_id} (status: #{status}, timeout: #{timeout})"
Gitlab::OptimisticLocking.retry_lock(build, 3) do |b| Gitlab::OptimisticLocking.retry_lock(build, 3) do |b|
b.drop b.drop(:stuck_or_timeout_failure)
end end
end end
end end
---
title: Implement `failure_reason` on `ci_builds`
merge_request: 13937
author:
type: added
class AddFailureReasonToCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_builds, :failure_reason, :integer
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170824162758) do ActiveRecord::Schema.define(version: 20170830125940) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -247,6 +247,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do ...@@ -247,6 +247,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.boolean "retried" t.boolean "retried"
t.integer "stage_id" t.integer "stage_id"
t.boolean "protected" t.boolean "protected"
t.integer "failure_reason"
end end
add_index "ci_builds", ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree add_index "ci_builds", ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree
......
...@@ -103,7 +103,7 @@ module API ...@@ -103,7 +103,7 @@ module API
when 'success' when 'success'
status.success! status.success!
when 'failed' when 'failed'
status.drop! status.drop!(:api_failure)
when 'canceled' when 'canceled'
status.cancel! status.cancel!
else else
......
...@@ -114,6 +114,8 @@ module API ...@@ -114,6 +114,8 @@ module API
requires :id, type: Integer, desc: %q(Job's ID) requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace) optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed) optional :state, type: String, desc: %q(Job's status: success, failed)
optional :failure_reason, type: String, values: CommitStatus.failure_reasons.keys,
desc: %q(Job's failure_reason)
end end
put '/:id' do put '/:id' do
job = authenticate_job! job = authenticate_job!
...@@ -127,7 +129,7 @@ module API ...@@ -127,7 +129,7 @@ module API
when 'success' when 'success'
job.success job.success
when 'failed' when 'failed'
job.drop job.drop(params[:failure_reason] || :unknown_failure)
end end
end end
......
...@@ -278,6 +278,7 @@ CommitStatus: ...@@ -278,6 +278,7 @@ CommitStatus:
- auto_canceled_by_id - auto_canceled_by_id
- retried - retried
- protected - protected
- failure_reason
Ci::Variable: Ci::Variable:
- id - id
- project_id - project_id
......
...@@ -443,4 +443,25 @@ describe CommitStatus do ...@@ -443,4 +443,25 @@ describe CommitStatus do
end end
end end
end end
describe 'set failure_reason when drop' do
let(:commit_status) { create(:commit_status, :created) }
subject do
commit_status.drop!(reason)
commit_status
end
context 'when failure_reason is nil' do
let(:reason) { }
it { is_expected.to be_unknown_failure }
end
context 'when failure_reason is script_failure' do
let(:reason) { :script_failure }
it { is_expected.to be_script_failure }
end
end
end end
...@@ -142,6 +142,9 @@ describe API::CommitStatuses do ...@@ -142,6 +142,9 @@ describe API::CommitStatuses do
expect(json_response['ref']).not_to be_empty expect(json_response['ref']).not_to be_empty
expect(json_response['target_url']).to be_nil expect(json_response['target_url']).to be_nil
expect(json_response['description']).to be_nil expect(json_response['description']).to be_nil
if status == 'failed'
expect(CommitStatus.find(json_response['id'])).to be_api_failure
end
end end
end end
end end
......
...@@ -626,13 +626,34 @@ describe API::Runner do ...@@ -626,13 +626,34 @@ describe API::Runner do
it 'mark job as succeeded' do it 'mark job as succeeded' do
update_job(state: 'success') update_job(state: 'success')
expect(job.reload.status).to eq 'success' job.reload
expect(job).to be_success
end end
it 'mark job as failed' do it 'mark job as failed' do
update_job(state: 'failed') update_job(state: 'failed')
expect(job.reload.status).to eq 'failed' job.reload
expect(job).to be_failed
expect(job).to be_unknown_failure
end
context 'when failure_reason is script_failure' do
before do
update_job(state: 'failed', failure_reason: 'script_failure')
job.reload
end
it { expect(job).to be_script_failure }
end
context 'when failure_reason is runner_system_failure' do
before do
update_job(state: 'failed', failure_reason: 'runner_system_failure')
job.reload
end
it { expect(job).to be_runner_system_failure }
end end
end end
......
...@@ -22,7 +22,7 @@ describe Ci::RetryBuildService do ...@@ -22,7 +22,7 @@ describe Ci::RetryBuildService do
%i[type lock_version target_url base_tags %i[type lock_version target_url base_tags
commit_id deployments erased_by_id last_deployment project_id commit_id deployments erased_by_id last_deployment project_id
runner_id tag_taggings taggings tags trigger_request_id runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried].freeze user_id auto_canceled_by_id retried failure_reason].freeze
shared_examples 'build duplication' do shared_examples 'build duplication' do
let(:stage) do let(:stage) do
......
...@@ -116,6 +116,7 @@ describe Projects::UpdatePagesService do ...@@ -116,6 +116,7 @@ describe Projects::UpdatePagesService do
expect(deploy_status.description) expect(deploy_status.description)
.to match(/artifacts for pages are too large/) .to match(/artifacts for pages are too large/)
expect(deploy_status).to be_script_failure
end end
end end
......
...@@ -6,27 +6,31 @@ describe StuckCiJobsWorker do ...@@ -6,27 +6,31 @@ describe StuckCiJobsWorker do
let(:worker) { described_class.new } let(:worker) { described_class.new }
let(:exclusive_lease_uuid) { SecureRandom.uuid } let(:exclusive_lease_uuid) { SecureRandom.uuid }
subject do
job.reload
job.status
end
before do before do
job.update!(status: status, updated_at: updated_at) job.update!(status: status, updated_at: updated_at)
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(exclusive_lease_uuid) allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(exclusive_lease_uuid)
end end
shared_examples 'job is dropped' do shared_examples 'job is dropped' do
it 'changes status' do before do
worker.perform worker.perform
is_expected.to eq('failed') job.reload
end
it "changes status" do
expect(job).to be_failed
expect(job).to be_stuck_or_timeout_failure
end end
end end
shared_examples 'job is unchanged' do shared_examples 'job is unchanged' do
it "doesn't change status" do before do
worker.perform worker.perform
is_expected.to eq(status) job.reload
end
it "doesn't change status" do
expect(job.status).to eq(status)
end end
end 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