Skip to content
Snippets Groups Projects
Commit 42ca9c6f authored by Tiger Watson's avatar Tiger Watson
Browse files

Add :preparing status to HasStatus

Introduces a new status for builds between :created and :pending
that will be used when builds require one or more prerequisite
actions to be completed before being picked up by a runner
(such as creating Kubernetes resources before deploying).

The existing :created > :pending transition is unchanged, so
only builds that require preparation will use the :preparing
status.
parent 250f6ad2
No related branches found
No related tags found
No related merge requests found
Showing
with 203 additions and 18 deletions
Loading
Loading
@@ -31,6 +31,7 @@
}
}
 
.ci-status-icon-preparing,
.ci-status-icon-running {
svg {
fill: $blue-400;
Loading
Loading
Loading
Loading
@@ -166,6 +166,7 @@
float: left;
 
.accept-merge-request {
&.ci-preparing,
&.ci-pending,
&.ci-running {
@include btn-blue;
Loading
Loading
Loading
Loading
@@ -793,6 +793,7 @@
@include mini-pipeline-graph-color($white, $orange-100, $orange-200, $orange-500, $orange-600, $orange-700);
}
 
&.ci-status-icon-preparing,
&.ci-status-icon-running {
@include mini-pipeline-graph-color($white, $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
}
Loading
Loading
Loading
Loading
@@ -44,6 +44,7 @@
}
 
&.ci-info,
&.ci-preparing,
&.ci-running {
@include status-color($blue-100, $blue-500, $blue-600);
}
Loading
Loading
Loading
Loading
@@ -82,10 +82,14 @@ module Ci
 
state_machine :status, initial: :created do
event :enqueue do
transition [:created, :skipped, :scheduled] => :pending
transition [:created, :preparing, :skipped, :scheduled] => :pending
transition [:success, :failed, :canceled] => :running
end
 
event :prepare do
transition any - [:preparing] => :preparing
end
event :run do
transition any - [:running] => :running
end
Loading
Loading
@@ -118,7 +122,7 @@ module Ci
# Do not add any operations to this state_machine
# Create a separate worker for each new operation
 
before_transition [:created, :pending] => :running do |pipeline|
before_transition [:created, :preparing, :pending] => :running do |pipeline|
pipeline.started_at = Time.now
end
 
Loading
Loading
@@ -141,7 +145,7 @@ module Ci
end
end
 
after_transition [:created, :pending] => :running do |pipeline|
after_transition [:created, :preparing, :pending] => :running do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
 
Loading
Loading
@@ -149,7 +153,7 @@ module Ci
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
 
after_transition [:created, :pending, :running] => :success do |pipeline|
after_transition [:created, :preparing, :pending, :running] => :success do |pipeline|
pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) }
end
 
Loading
Loading
@@ -597,6 +601,7 @@ module Ci
retry_optimistic_lock(self) do
case latest_builds_status.to_s
when 'created' then nil
when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
Loading
Loading
Loading
Loading
@@ -39,10 +39,14 @@ module Ci
 
state_machine :status, initial: :created do
event :enqueue do
transition created: :pending
transition [:created, :preparing] => :pending
transition [:success, :failed, :canceled, :skipped] => :running
end
 
event :prepare do
transition any - [:preparing] => :preparing
end
event :run do
transition any - [:running] => :running
end
Loading
Loading
@@ -76,6 +80,7 @@ module Ci
retry_optimistic_lock(self) do
case statuses.latest.status
when 'created' then nil
when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
Loading
Loading
Loading
Loading
@@ -66,7 +66,7 @@ class CommitStatus < ActiveRecord::Base
end
 
event :enqueue do
transition [:created, :skipped, :manual, :scheduled] => :pending
transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending
end
 
event :run do
Loading
Loading
@@ -74,26 +74,26 @@ class CommitStatus < ActiveRecord::Base
end
 
event :skip do
transition [:created, :pending] => :skipped
transition [:created, :preparing, :pending] => :skipped
end
 
event :drop do
transition [:created, :pending, :running, :scheduled] => :failed
transition [:created, :preparing, :pending, :running, :scheduled] => :failed
end
 
event :success do
transition [:created, :pending, :running] => :success
transition [:created, :preparing, :pending, :running] => :success
end
 
event :cancel do
transition [:created, :pending, :running, :manual, :scheduled] => :canceled
transition [:created, :preparing, :pending, :running, :manual, :scheduled] => :canceled
end
 
before_transition [:created, :skipped, :manual, :scheduled] => :pending do |commit_status|
before_transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status|
commit_status.queued_at = Time.now
end
 
before_transition [:created, :pending] => :running do |commit_status|
before_transition [:created, :preparing, :pending] => :running do |commit_status|
commit_status.started_at = Time.now
end
 
Loading
Loading
Loading
Loading
@@ -5,14 +5,14 @@ module HasStatus
 
DEFAULT_STATUS = 'created'.freeze
BLOCKED_STATUS = %w[manual scheduled].freeze
AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual scheduled].freeze
AVAILABLE_STATUSES = %w[created preparing pending running success failed canceled skipped manual scheduled].freeze
STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
ACTIVE_STATUSES = %w[pending running].freeze
ACTIVE_STATUSES = %w[preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
ORDERED_STATUSES = %w[failed pending running manual scheduled canceled success skipped created].freeze
ORDERED_STATUSES = %w[failed preparing pending running manual scheduled canceled success skipped created].freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
scheduled: 8 }.freeze
scheduled: 8, preparing: 9 }.freeze
 
UnknownStatusError = Class.new(StandardError)
 
Loading
Loading
@@ -26,6 +26,7 @@ module HasStatus
success = scope_relevant.success.select('count(*)').to_sql
manual = scope_relevant.manual.select('count(*)').to_sql
scheduled = scope_relevant.scheduled.select('count(*)').to_sql
preparing = scope_relevant.preparing.select('count(*)').to_sql
pending = scope_relevant.pending.select('count(*)').to_sql
running = scope_relevant.running.select('count(*)').to_sql
skipped = scope_relevant.skipped.select('count(*)').to_sql
Loading
Loading
@@ -37,12 +38,14 @@ module HasStatus
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created'
WHEN (#{builds})=(#{preparing}) THEN 'preparing'
WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{manual})>0 THEN 'manual'
WHEN (#{scheduled})>0 THEN 'scheduled'
WHEN (#{preparing})>0 THEN 'preparing'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
Loading
Loading
@@ -70,6 +73,7 @@ module HasStatus
 
state_machine :status, initial: :created do
state :created, value: 'created'
state :preparing, value: 'preparing'
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
Loading
Loading
@@ -81,6 +85,7 @@ module HasStatus
end
 
scope :created, -> { where(status: 'created') }
scope :preparing, -> { where(status: 'preparing') }
scope :relevant, -> { where(status: AVAILABLE_STATUSES - ['created']) }
scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') }
Loading
Loading
@@ -90,14 +95,14 @@ module HasStatus
scope :skipped, -> { where(status: 'skipped') }
scope :manual, -> { where(status: 'manual') }
scope :scheduled, -> { where(status: 'scheduled') }
scope :alive, -> { where(status: [:created, :pending, :running]) }
scope :alive, -> { where(status: [:created, :preparing, :pending, :running]) }
scope :created_or_pending, -> { where(status: [:created, :pending]) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
 
scope :cancelable, -> do
where(status: [:running, :pending, :created, :scheduled])
where(status: [:running, :preparing, :pending, :created, :scheduled])
end
end
 
Loading
Loading
Loading
Loading
@@ -15,6 +15,7 @@ module Gitlab
failed: '#e05d44',
running: '#dfb317',
pending: '#dfb317',
preparing: '#dfb317',
canceled: '#9f9f9f',
skipped: '#9f9f9f',
unknown: '#9f9f9f'
Loading
Loading
Loading
Loading
@@ -11,6 +11,7 @@ module Gitlab
Status::Build::Manual,
Status::Build::Canceled,
Status::Build::Created,
Status::Build::Preparing,
Status::Build::Pending,
Status::Build::Skipped],
[Status::Build::Cancelable,
Loading
Loading
# frozen_string_literal: true
module Gitlab
module Ci
module Status
module Build
class Preparing < Status::Extended
##
# TODO: image is shared with 'pending'
# until we get a dedicated one
#
def illustration
{
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: _('This job is preparing to start'),
content: _('This job is performing tasks that must complete before it can start')
}
end
def self.matches?(build, _)
build.preparing?
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Ci
module Status
class Preparing < Status::Core
def text
s_('CiStatusText|preparing')
end
def label
s_('CiStatusLabel|preparing')
end
##
# TODO: shared with 'created'
# until we get one for 'preparing'
#
def icon
'status_created'
end
##
# TODO: shared with 'created'
# until we get one for 'preparing'
#
def favicon
'favicon_status_created'
end
end
end
end
end
Loading
Loading
@@ -1512,6 +1512,9 @@ msgstr ""
msgid "CiStatusLabel|pending"
msgstr ""
 
msgid "CiStatusLabel|preparing"
msgstr ""
msgid "CiStatusLabel|skipped"
msgstr ""
 
Loading
Loading
@@ -1545,6 +1548,9 @@ msgstr ""
msgid "CiStatusText|pending"
msgstr ""
 
msgid "CiStatusText|preparing"
msgstr ""
msgid "CiStatusText|skipped"
msgstr ""
 
Loading
Loading
@@ -7864,6 +7870,12 @@ msgstr ""
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
 
msgid "This job is performing tasks that must complete before it can start"
msgstr ""
msgid "This job is preparing to start"
msgstr ""
msgid "This job is stuck because you don't have any active runners online with any of these tags assigned to them:"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -75,6 +75,10 @@ FactoryBot.define do
status 'created'
end
 
trait :preparing do
status 'preparing'
end
trait :scheduled do
schedulable
status 'scheduled'
Loading
Loading
Loading
Loading
@@ -50,6 +50,14 @@ FactoryBot.define do
failure_reason :config_error
end
 
trait :created do
status :created
end
trait :preparing do
status :preparing
end
trait :blocked do
status :manual
end
Loading
Loading
Loading
Loading
@@ -33,6 +33,10 @@ FactoryBot.define do
status 'pending'
end
 
trait :preparing do
status 'preparing'
end
trait :created do
status 'created'
end
Loading
Loading
Loading
Loading
@@ -41,6 +41,24 @@ describe 'Pipeline Badge' do
end
end
 
context 'when the pipeline is preparing' do
let!(:job) { create(:ci_build, status: 'created', pipeline: pipeline) }
before do
# Prevent skipping directly to 'pending'
allow(Ci::BuildPrepareWorker).to receive(:perform_async)
end
it 'displays the preparing badge' do
job.prepare
visit pipeline_project_badges_path(project, ref: ref, format: :svg)
expect(page.status_code).to eq(200)
expect_badge('preparing')
end
end
context 'when the pipeline is running' do
it 'shows displays so on the badge' do
create(:ci_build, pipeline: pipeline, name: 'second build', status_event: 'run')
Loading
Loading
Loading
Loading
@@ -21,6 +21,11 @@ describe 'Pipeline', :js do
pipeline: pipeline, stage: 'test', name: 'test')
end
 
let!(:build_preparing) do
create(:ci_build, :preparing,
pipeline: pipeline, stage: 'deploy', name: 'prepare')
end
let!(:build_running) do
create(:ci_build, :running,
pipeline: pipeline, stage: 'deploy', name: 'deploy')
Loading
Loading
@@ -97,6 +102,24 @@ describe 'Pipeline', :js do
end
end
 
context 'when pipeline has preparing builds' do
it 'shows a preparing icon and a cancel action' do
page.within('#ci-badge-prepare') do
expect(page).to have_selector('.js-ci-status-icon-preparing')
expect(page).to have_selector('.js-icon-cancel')
expect(page).to have_content('prepare')
end
end
it 'cancels the preparing build and shows retry button' do
find('#ci-badge-deploy .ci-action-icon-container').click
page.within('#ci-badge-deploy') do
expect(page).to have_css('.js-icon-retry')
end
end
end
context 'when pipeline has successful builds' do
it 'shows the success icon and a retry action for the successful build' do
page.within('#ci-badge-build') do
Loading
Loading
Loading
Loading
@@ -282,6 +282,30 @@ describe 'Pipelines', :js do
end
 
context 'for generic statuses' do
context 'when preparing' do
let!(:pipeline) do
create(:ci_empty_pipeline,
status: 'preparing', project: project)
end
let!(:status) do
create(:generic_commit_status,
:preparing, pipeline: pipeline)
end
before do
visit_project_pipelines
end
it 'is cancelable' do
expect(page).to have_selector('.js-pipelines-cancel-button')
end
it 'shows the pipeline as preparing' do
expect(page).to have_selector('.ci-preparing')
end
end
context 'when running' do
let!(:running) do
create(:generic_commit_status,
Loading
Loading
Loading
Loading
@@ -59,6 +59,16 @@ describe Gitlab::Badge::Pipeline::Template do
end
end
 
context 'when status is preparing' do
before do
allow(badge).to receive(:status).and_return('preparing')
end
it 'has expected color' do
expect(template.value_color).to eq '#dfb317'
end
end
context 'when status is unknown' do
before do
allow(badge).to receive(:status).and_return('unknown')
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment