diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index cf5df2ca354..3f2630798f3 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -22,7 +22,6 @@ 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 @@ -168,12 +167,26 @@ module Ci transition scheduled: :manual end - before_transition created: :scheduled do |build| - build.build_build_schedule(execute_at: build.execute_at) + event :enqueue_scheduled do + transition scheduled: :pending do + validate do |build| + build.scheduled_at && build.scheduled_at < Time.now + end + end end before_transition scheduled: any do |build| - build.build_schedule.delete + build.scheduled_at = nil + end + + before_transition created: :scheduled do |build| + build.scheduled_at = build.get_scheduled_at + end + + after_transition created: :scheduled do |build| + build.run_after_commit do + Ci::BuildScheduleWorker.perform_at(build.scheduled_at, build.id) + end end after_transition any => [:pending] do |build| @@ -250,7 +263,7 @@ module Ci self.when == 'delayed' && options[:start_in].present? end - def execute_at + def get_scheduled_at ChronicDuration.parse(options[:start_in])&.seconds&.from_now end diff --git a/app/models/ci/build_schedule.rb b/app/models/ci/build_schedule.rb deleted file mode 100644 index 4128fade86c..00000000000 --- a/app/models/ci/build_schedule.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module Ci - class BuildSchedule < ActiveRecord::Base - extend Gitlab::Ci::Model - include Importable - include AfterCommitQueue - - belongs_to :build - - validate :schedule_at_future - - after_create :schedule, unless: :importing? - - scope :stale, -> { where("execute_at < ?", Time.now) } - - def execute_in - [0, self.execute_at - Time.now].max - end - - private - - def schedule_at_future - if self.execute_at < Time.now - errors.add(:execute_at, "Excute point must be somewhere in the future") - end - end - - def schedule - run_after_commit do - Ci::BuildScheduleWorker.perform_at(self.execute_at, self.build_id) - end - end - end -end diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb index 5331cdf632b..4005840ce58 100644 --- a/app/presenters/ci/build_presenter.rb +++ b/app/presenters/ci/build_presenter.rb @@ -35,6 +35,10 @@ module Ci "#{subject.name} - #{detailed_status.status_tooltip}" end + def execute_in + [0, scheduled_at - Time.now].max + end + private def tooltip_for_badge diff --git a/app/services/ci/run_scheduled_build_service.rb b/app/services/ci/run_scheduled_build_service.rb new file mode 100644 index 00000000000..8e4a628296f --- /dev/null +++ b/app/services/ci/run_scheduled_build_service.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Ci + class RunScheduledBuildService < ::BaseService + def execute(build) + unless can?(current_user, :update_build, build) + raise Gitlab::Access::AccessDeniedError + end + + build.enqueue_scheduled! + end + end +end diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 1ba8b698fe2..c706703ae6f 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -104,9 +104,9 @@ - elsif job.scheduled? .btn-group .btn.btn-default.has-tooltip{ disabled: true, - title: job.build_schedule.execute_at } + title: job.scheduled_at } = sprite_icon('planning') - = duration_in_numbers(job.build_schedule.execute_in) + = duration_in_numbers(job.execute_in) .btn.btn-default.btn-build.has-tooltip{ title: s_('DelayedJobs|Start now') } = sprite_icon('play') .btn.btn-default.btn-build.has-tooltip{ title: s_('DelayedJobs|Unschedule') } diff --git a/app/workers/ci/build_schedule_worker.rb b/app/workers/ci/build_schedule_worker.rb index 0d17a960c00..2a2d2bff282 100644 --- a/app/workers/ci/build_schedule_worker.rb +++ b/app/workers/ci/build_schedule_worker.rb @@ -6,10 +6,9 @@ module Ci include PipelineQueue def perform(build_id) - ::Ci::Build.find_by(id: build_id).try do |build| - break unless build.scheduled? - - Ci::PlayBuildService.new(build.project, build.user).execute(build) + ::Ci::Build.find_by_id(build_id).try do |build| + Ci::RunScheduledBuildService + .new(build.project, build.user).execute(build) end end end diff --git a/lib/gitlab/ci/status/build/scheduled.rb b/lib/gitlab/ci/status/build/scheduled.rb index 05a97b1de47..270a2706c87 100644 --- a/lib/gitlab/ci/status/build/scheduled.rb +++ b/lib/gitlab/ci/status/build/scheduled.rb @@ -23,7 +23,7 @@ module Gitlab private def execute_in - Time.at(subject.build_schedule.execute_in).utc.strftime("%H:%M:%S") + Time.at(subject.scheduled_at).utc.strftime("%H:%M:%S") end end end diff --git a/scheduled_job_fixture.rb b/scheduled_job_fixture.rb index 7389e63a0da..9ed59d337f7 100644 --- a/scheduled_job_fixture.rb +++ b/scheduled_job_fixture.rb @@ -64,15 +64,15 @@ cleanup: # # ### Reproduce the scenario ~ when all stages succeeded ~ # -# 1. ScheduledJobFixture.new(29, 1).create_pipeline('master') -# 1. ScheduledJobFixture.new(29, 1).finish_stage_until('test') +# 1. ScheduledJobFixture.new(16, 1).create_pipeline('master') +# 1. ScheduledJobFixture.new(16, 1).finish_stage_until('test') # 1. Wait until rollout 10% job is triggered -# 1. ScheduledJobFixture.new(29, 1).finish_stage_until('rollout 10%') +# 1. ScheduledJobFixture.new(16, 1).finish_stage_until('rollout 10%') # 1. Wait until rollout 50% job is triggered -# 1. ScheduledJobFixture.new(29, 1).finish_stage_until('rollout 50%') +# 1. ScheduledJobFixture.new(16, 1).finish_stage_until('rollout 50%') # 1. Wait until rollout 100% job is triggered -# 1. ScheduledJobFixture.new(29, 1).finish_stage_until('rollout 100%') -# 1. ScheduledJobFixture.new(29, 1).finish_stage_until('cleanup') +# 1. ScheduledJobFixture.new(16, 1).finish_stage_until('rollout 100%') +# 1. ScheduledJobFixture.new(16, 1).finish_stage_until('cleanup') # # Expectation: Users see a succeccful pipeline #