Commit 37d6d1e4 authored by Shinya Maeda's avatar Shinya Maeda

basic components

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