Squashed commit of the following:

commit 9d9594ba20097dc4598f7eb42a9f9d78d73eae54
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Thu Sep 13 20:18:31 2018 +0900

    Cancel scheduled jobs

commit f31c7172e07a9eb03b58c1e62eaa18cda4064aa6
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Thu Sep 13 11:18:42 2018 +0900

    Add Ci::BuildSchedule

commit fb6b3ca638f40f9e1ee38b1fdd892bda4f6fede7
Author: Shinya Maeda <shinya@gitlab.com>
Date:   Wed Sep 12 20:02:50 2018 +0900

    Scheduled jobs
This commit is contained in:
Shinya Maeda 2018-09-17 18:26:22 +09:00 committed by Alessio Caiazza
parent d5184e0d8b
commit 3fc4c096a5
16 changed files with 160 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -22,6 +22,7 @@ module Ci
}.freeze
has_one :last_deployment, -> { order('deployments.id DESC') }, as: :deployable, class_name: 'Deployment'
has_one :build_schedule, class_name: 'Ci::BuildSchedule', foreign_key: :build_id
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id
@ -184,6 +185,12 @@ module Ci
end
end
after_transition any => [:manual] do |build|
build.run_after_commit do
build.schedule_delayed_execution
end
end
before_transition any => [:failed] do |build|
next unless build.project
next if build.retries_max.zero?
@ -229,6 +236,20 @@ module Ci
action? && (manual? || retryable?)
end
def autoplay?
manual? && options[:autoplay_in].present?
end
def autoplay_at
ChronicDuration.parse(options[:autoplay_in])&.seconds&.from_now
end
def schedule_delayed_execution
return unless autoplay?
create_build_schedule!(execute_at: autoplay_at)
end
def action?
self.when == 'manual'
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Ci
class BuildSchedule < ActiveRecord::Base
extend Gitlab::Ci::Model
include Importable
include AfterCommitQueue
belongs_to :build
after_create :schedule, unless: :importing?
def execute_in
self.execute_at - Time.now
end
private
def schedule
run_after_commit do
Ci::BuildScheduleWorker.perform_at(self.execute_at, self.build_id)
end
end
end
end

View file

@ -92,7 +92,8 @@ module HasStatus
scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
scope :cancelable, -> do
where(status: [:running, :pending, :created])
where("status IN ('running', 'pending', 'created') OR " \
"(status = 'manual' AND EXISTS (select 1 from ci_build_schedules where ci_builds.id = ci_build_schedules.build_id))")
end
end

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -60,6 +60,7 @@
- pipeline_default:build_trace_sections
- pipeline_default:pipeline_metrics
- pipeline_default:pipeline_notification
- pipeline_default:ci_build_schedule
- pipeline_hooks:build_hooks
- pipeline_hooks:pipeline_hooks
- pipeline_processing:build_finished

View file

@ -9,6 +9,7 @@ class BuildFinishedWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
build&.build_schedule&.delete
# We execute that in sync as this access the files in order to access local file, and reduce IO
BuildTraceSectionsWorker.new.perform(build.id)
BuildCoverageWorker.new.perform(build.id)

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Ci
class BuildScheduleWorker
include ApplicationWorker
include PipelineQueue
def perform(build_id)
::Ci::Build.preload(:build_schedule).find_by(id: build_id).try do |build|
break unless build.build_schedule.present?
Ci::PlayBuildService.new(build.project, build.user).execute(build)
end
end
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
class CreateBuildSchedules < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def change
create_table :ci_build_schedules, id: :bigserial do |t|
t.integer :build_id, null: false
t.datetime :execute_at, null: false
t.foreign_key :ci_builds, column: :build_id, on_delete: :cascade
t.index :build_id, unique: true
end
end
end

View file

@ -260,6 +260,13 @@ ActiveRecord::Schema.define(version: 20180924141949) do
add_index "chat_teams", ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
create_table "ci_build_schedules", id: :bigserial, force: :cascade do |t|
t.integer "build_id", null: false
t.datetime "execute_at", null: false
end
add_index "ci_build_schedules", ["build_id"], name: "index_ci_build_schedules_on_build_id", unique: true, using: :btree
create_table "ci_build_trace_chunks", id: :bigserial, force: :cascade do |t|
t.integer "build_id", null: false
t.integer "chunk_index", null: false
@ -2288,6 +2295,7 @@ ActiveRecord::Schema.define(version: 20180924141949) do
add_foreign_key "boards", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "boards", "projects", name: "fk_f15266b5f9", on_delete: :cascade
add_foreign_key "chat_teams", "namespaces", on_delete: :cascade
add_foreign_key "ci_build_schedules", "ci_builds", column: "build_id", on_delete: :cascade
add_foreign_key "ci_build_trace_chunks", "ci_builds", column: "build_id", on_delete: :cascade
add_foreign_key "ci_build_trace_section_names", "projects", on_delete: :cascade
add_foreign_key "ci_build_trace_sections", "ci_build_trace_section_names", column: "section_name_id", name: "fk_264e112c66", on_delete: :cascade

View file

@ -10,7 +10,7 @@ module Gitlab
include Attributable
ALLOWED_KEYS = %i[tags script only except type image services
allow_failure type stage when artifacts cache
allow_failure type stage when autoplay_in artifacts cache
dependencies before_script after_script variables
environment coverage retry extends].freeze
@ -34,6 +34,14 @@ module Gitlab
validates :dependencies, array_of_strings: true
validates :extends, type: String
with_options if: :manual_action? do
validates :autoplay_in, duration: true, allow_nil: true
end
with_options unless: :manual_action? do
validates :autoplay_in, presence: false
end
end
end
@ -84,7 +92,7 @@ module Gitlab
:artifacts, :commands, :environment, :coverage, :retry
attributes :script, :tags, :allow_failure, :when, :dependencies,
:retry, :extends
:retry, :extends, :autoplay_in
def compose!(deps = nil)
super do

View file

@ -5,6 +5,7 @@ module Gitlab
class Factory < Status::Factory
def self.extended_statuses
[[Status::Build::Erased,
Status::Build::ManualWithAutoPlay,
Status::Build::Manual,
Status::Build::Canceled,
Status::Build::Created,

View file

@ -0,0 +1,52 @@
module Gitlab
module Ci
module Status
module Build
class ManualWithAutoPlay < Status::Extended
###
# TODO: Those are random values. We have to fix accoding to the UX review
###
###
# Core override
###
def text
s_('CiStatusText|scheduled')
end
def label
s_('CiStatusLabel|scheduled')
end
def icon
'timer'
end
def favicon
'favicon_status_manual_with_auto_play'
end
###
# Extension override
###
def illustration
{
image: 'illustrations/canceled-job_empty.svg',
size: 'svg-394',
title: _('This job is a scheduled job with manual actions!'),
content: _('auto playyyyyyyyyyyyyy! This job depends on a user to trigger its process. Often they are used to deploy code to production environments')
}
end
def status_tooltip
@status.status_tooltip + " (scheulded) : Execute in #{subject.build_schedule.execute_in.round} sec"
end
def self.matches?(build, user)
build.autoplay? && !build.canceled?
end
end
end
end
end
end

View file

@ -49,7 +49,8 @@ module Gitlab
script: job[:script],
after_script: job[:after_script],
environment: job[:environment],
retry: job[:retry]
retry: job[:retry],
autoplay_in: job[:autoplay_in],
}.compact }
end