basic components
This commit is contained in:
parent
5f715f1d32
commit
37d6d1e461
|
@ -9,15 +9,13 @@ module Ci
|
|||
|
||||
def schedule_next_run!
|
||||
next_time = Ci::CronParser.new(cron, cron_time_zone).next_time_from_now
|
||||
update(:next_run_at => next_time) if next_time.present?
|
||||
end
|
||||
|
||||
def valid_ref?
|
||||
true #TODO:
|
||||
if next_time.present?
|
||||
update_attributes(next_run_at: next_time)
|
||||
end
|
||||
end
|
||||
|
||||
def update_last_run!
|
||||
update(:last_run_at => Time.now)
|
||||
update_attributes(last_run_at: Time.now)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,14 +2,14 @@ module Ci
|
|||
class CreatePipelineService < BaseService
|
||||
attr_reader :pipeline
|
||||
|
||||
def execute(ignore_skip_ci: false, save_on_errors: true, trigger_request: nil)
|
||||
def execute(ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, scheduled_trigger: false)
|
||||
@pipeline = Ci::Pipeline.new(
|
||||
project: project,
|
||||
ref: ref,
|
||||
sha: sha,
|
||||
before_sha: before_sha,
|
||||
tag: tag?,
|
||||
trigger_requests: Array(trigger_request),
|
||||
trigger_requests: (scheduled_trigger) ? [] : Array(trigger_request),
|
||||
user: current_user
|
||||
)
|
||||
|
||||
|
@ -17,8 +17,10 @@ module Ci
|
|||
return error('Pipeline is disabled')
|
||||
end
|
||||
|
||||
unless trigger_request || can?(current_user, :create_pipeline, project)
|
||||
return error('Insufficient permissions to create a new pipeline')
|
||||
unless scheduled_trigger
|
||||
unless trigger_request || can?(current_user, :create_pipeline, project)
|
||||
return error('Insufficient permissions to create a new pipeline')
|
||||
end
|
||||
end
|
||||
|
||||
unless branch? || tag?
|
||||
|
|
|
@ -3,15 +3,15 @@ class ScheduledTriggerWorker
|
|||
include CronjobQueue
|
||||
|
||||
def perform
|
||||
# TODO: Update next_run_at
|
||||
|
||||
Ci::ScheduledTriggers.where("next_run_at < ?", Time.now).find_each do |trigger|
|
||||
Ci::ScheduledTrigger.where("next_run_at < ?", Time.now).find_each do |trigger|
|
||||
begin
|
||||
Ci::CreateTriggerRequestService.new.execute(trigger.project, trigger, trigger.ref)
|
||||
Ci::CreatePipelineService.new(trigger.project, trigger.owner, ref: trigger.ref).
|
||||
execute(ignore_skip_ci: true, scheduled_trigger: true)
|
||||
rescue => e
|
||||
Rails.logger.error "#{trigger.id}: Failed to trigger job: #{e.message}"
|
||||
ensure
|
||||
trigger.schedule_next_run!
|
||||
trigger.update_last_run!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,42 +1,58 @@
|
|||
FactoryGirl.define do
|
||||
factory :ci_scheduled_trigger, class: Ci::ScheduledTrigger do
|
||||
project factory: :empty_project
|
||||
project factory: :project
|
||||
owner factory: :user
|
||||
ref 'master'
|
||||
|
||||
trait :force_triggable do
|
||||
next_run_at Time.now - 1.month
|
||||
end
|
||||
|
||||
trait :cron_nightly_build do
|
||||
cron '0 1 * * *'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
next_run_at do # TODO: Use CronParser
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
time = time + 1.day if time.hour > 1
|
||||
time = time.change(sec: 0, min: 0, hour: 1)
|
||||
time
|
||||
end
|
||||
end
|
||||
|
||||
trait :cron_weekly_build do
|
||||
cron '0 1 * * 5'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
# TODO: next_run_at
|
||||
end
|
||||
|
||||
trait :cron_monthly_build do
|
||||
cron '0 1 22 * *'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
# TODO: next_run_at
|
||||
end
|
||||
|
||||
trait :cron_every_5_minutes do
|
||||
cron '*/5 * * * *'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
# TODO: next_run_at
|
||||
end
|
||||
|
||||
trait :cron_every_5_hours do
|
||||
cron '* */5 * * *'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
# TODO: next_run_at
|
||||
end
|
||||
|
||||
trait :cron_every_5_days do
|
||||
cron '* * */5 * *'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
# TODO: next_run_at
|
||||
end
|
||||
|
||||
trait :cron_every_5_months do
|
||||
cron '* * * */5 *'
|
||||
cron_time_zone 'Europe/Istanbul'
|
||||
# TODO: next_run_at
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,91 +6,62 @@ module Ci
|
|||
subject { described_class.new(cron, cron_time_zone).next_time_from_now }
|
||||
|
||||
context 'when cron and cron_time_zone are valid' do
|
||||
context 'at 00:00, 00:10, 00:20, 00:30, 00:40, 00:50' do
|
||||
let(:cron) { '*/10 * * * *' }
|
||||
let(:cron_time_zone) { 'US/Pacific' }
|
||||
context 'when specific time' do
|
||||
let(:cron) { '3 4 5 6 *' }
|
||||
let(:cron_time_zone) { 'Europe/London' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
time = time + 10.minutes
|
||||
time = time.change(sec: 0, min: time.min-time.min%10)
|
||||
is_expected.to eq(time)
|
||||
it 'returns exact time in the future' do
|
||||
expect(subject).to be > Time.now.in_time_zone(cron_time_zone)
|
||||
expect(subject.min).to eq(3)
|
||||
expect(subject.hour).to eq(4)
|
||||
expect(subject.day).to eq(5)
|
||||
expect(subject.month).to eq(6)
|
||||
end
|
||||
end
|
||||
|
||||
context 'at 10:00, 20:00' do
|
||||
let(:cron) { '0 */10 * * *' }
|
||||
let(:cron_time_zone) { 'US/Pacific' }
|
||||
context 'when specific day of week' do
|
||||
let(:cron) { '* * * * 0' }
|
||||
let(:cron_time_zone) { 'Europe/London' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
time = time + 10.hours
|
||||
time = time.change(sec: 0, min: 0, hour: time.hour-time.hour%10)
|
||||
is_expected.to eq(time)
|
||||
it 'returns exact day of week in the future' do
|
||||
expect(subject).to be > Time.now.in_time_zone(cron_time_zone)
|
||||
expect(subject.wday).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron is every 10 days' do
|
||||
let(:cron) { '0 0 */10 * *' }
|
||||
context 'when slash used' do
|
||||
let(:cron) { '*/10 */6 */10 */10 *' }
|
||||
let(:cron_time_zone) { 'US/Pacific' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
time = time + 10.days
|
||||
time = time.change(sec: 0, min: 0, hour: 0, day: time.day-time.day%10)
|
||||
is_expected.to eq(time)
|
||||
it 'returns exact minute' do
|
||||
expect(subject).to be > Time.now.in_time_zone(cron_time_zone)
|
||||
expect(subject.min).to be_in([0, 10, 20, 30, 40, 50])
|
||||
expect(subject.hour).to be_in([0, 6, 12, 18])
|
||||
expect(subject.day).to be_in([1, 11, 21, 31])
|
||||
expect(subject.month).to be_in([1, 11])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron is every week 2:00 AM' do
|
||||
let(:cron) { '0 2 * * *' }
|
||||
context 'when range used' do
|
||||
let(:cron) { '0,20,40 * 1-5 * *' }
|
||||
let(:cron_time_zone) { 'US/Pacific' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
is_expected.to eq(time.change(sec: 0, min: 0, hour: 2, day: time.day+1))
|
||||
expect(subject).to be > Time.now.in_time_zone(cron_time_zone)
|
||||
expect(subject.min).to be_in([0, 20, 40])
|
||||
expect(subject.day).to be_in((1..5).to_a)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron_time_zone is US/Pacific' do
|
||||
let(:cron) { '0 1 * * *' }
|
||||
let(:cron) { '* * * * *' }
|
||||
let(:cron_time_zone) { 'US/Pacific' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
is_expected.to eq(time.change(sec: 0, min: 0, hour: 1, day: time.day+1))
|
||||
expect(subject).to be > Time.now.in_time_zone(cron_time_zone)
|
||||
expect(subject.utc_offset/60/60).to eq(-7)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron_time_zone is Europe/London' do
|
||||
let(:cron) { '0 1 * * *' }
|
||||
let(:cron_time_zone) { 'Europe/London' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
is_expected.to eq(time.change(sec: 0, min: 0, hour: 1, day: time.day+1))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron_time_zone is Asia/Tokyo' do
|
||||
let(:cron) { '0 1 * * *' }
|
||||
let(:cron_time_zone) { 'Asia/Tokyo' }
|
||||
|
||||
it 'returns next time from now' do
|
||||
time = Time.now.in_time_zone(cron_time_zone)
|
||||
is_expected.to eq(time.change(sec: 0, min: 0, hour: 1, day: time.day+1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron is given and cron_time_zone is not given' do
|
||||
let(:cron) { '0 1 * * *' }
|
||||
|
||||
it 'returns next time from now in utc' do
|
||||
obj = described_class.new(cron).next_time_from_now
|
||||
time = Time.now.in_time_zone('UTC')
|
||||
expect(obj).to eq(time.change(sec: 0, min: 0, hour: 1, day: time.day+1))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron and cron_time_zone are invalid' do
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require 'rufus-scheduler' # Included in sidekiq-cron
|
||||
|
||||
describe Ci::ScheduledTrigger, models: true do
|
||||
|
||||
|
@ -9,30 +8,22 @@ describe Ci::ScheduledTrigger, models: true do
|
|||
end
|
||||
|
||||
describe '#schedule_next_run!' do
|
||||
context 'when cron and cron_time_zone are vaild' do
|
||||
context 'when nightly build' do
|
||||
it 'schedules next run' do
|
||||
scheduled_trigger = create(:ci_scheduled_trigger, :cron_nightly_build)
|
||||
scheduled_trigger.schedule_next_run!
|
||||
puts "scheduled_trigger: #{scheduled_trigger.inspect}"
|
||||
subject { scheduled_trigger.schedule_next_run! }
|
||||
|
||||
expect(scheduled_trigger.cron).to be_nil
|
||||
end
|
||||
end
|
||||
let(:scheduled_trigger) { create(:ci_scheduled_trigger, :cron_nightly_build, next_run_at: nil) }
|
||||
|
||||
context 'when weekly build' do
|
||||
|
||||
end
|
||||
|
||||
context 'when monthly build' do
|
||||
|
||||
end
|
||||
it 'updates next_run_at' do
|
||||
is_expected.not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cron and cron_time_zone are invaild' do
|
||||
it 'schedules nothing' do
|
||||
describe '#update_last_run!' do
|
||||
subject { scheduled_trigger.update_last_run! }
|
||||
|
||||
end
|
||||
let(:scheduled_trigger) { create(:ci_scheduled_trigger, :cron_nightly_build, last_run_at: nil) }
|
||||
|
||||
it 'updates last_run_at' do
|
||||
is_expected.not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -214,5 +214,9 @@ describe Ci::CreatePipelineService, services: true do
|
|||
expect(Environment.find_by(name: "review/master")).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scheduled_trigger' do
|
||||
# TODO: spec if approved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,57 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ScheduledTriggerWorker do
|
||||
subject { described_class.new.perform }
|
||||
let(:worker) { described_class.new }
|
||||
|
||||
context '#perform' do # TODO:
|
||||
it 'does' do
|
||||
is_expected.to be_nil
|
||||
before do
|
||||
stub_ci_pipeline_to_return_yaml_file
|
||||
end
|
||||
|
||||
context 'when there is a scheduled trigger within next_run_at' do
|
||||
before do
|
||||
create(:ci_scheduled_trigger, :cron_nightly_build, :force_triggable)
|
||||
worker.perform
|
||||
end
|
||||
|
||||
it 'creates a new pipeline' do
|
||||
expect(Ci::Pipeline.last.status).to eq('pending')
|
||||
end
|
||||
|
||||
it 'schedules next_run_at' do
|
||||
scheduled_trigger2 = create(:ci_scheduled_trigger, :cron_nightly_build)
|
||||
expect(Ci::ScheduledTrigger.last.next_run_at).to eq(scheduled_trigger2.next_run_at)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are no scheduled triggers within next_run_at' do
|
||||
let!(:scheduled_trigger) { create(:ci_scheduled_trigger, :cron_nightly_build) }
|
||||
|
||||
before do
|
||||
worker.perform
|
||||
end
|
||||
|
||||
it 'do not create a new pipeline' do
|
||||
expect(Ci::Pipeline.all).to be_empty
|
||||
end
|
||||
|
||||
it 'do not reschedule next_run_at' do
|
||||
expect(Ci::ScheduledTrigger.last.next_run_at).to eq(scheduled_trigger.next_run_at)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when next_run_at is nil' do
|
||||
let!(:scheduled_trigger) { create(:ci_scheduled_trigger, :cron_nightly_build, next_run_at: nil) }
|
||||
|
||||
before do
|
||||
worker.perform
|
||||
end
|
||||
|
||||
it 'do not create a new pipeline' do
|
||||
expect(Ci::Pipeline.all).to be_empty
|
||||
end
|
||||
|
||||
it 'do not reschedule next_run_at' do
|
||||
expect(Ci::ScheduledTrigger.last.next_run_at).to eq(scheduled_trigger.next_run_at)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue