summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShinya Maeda <gitlab.shinyamaeda@gmail.com>2017-03-24 00:18:13 +0900
committerShinya Maeda <gitlab.shinyamaeda@gmail.com>2017-04-06 23:46:58 +0900
commit37d6d1e46130f44f2fe05171b814b5682696839c (patch)
tree5763dde8262d2d513145eadd70236dbebdba98c1
parent5f715f1d32c6f5ce25b3721bde8f476173afadc8 (diff)
downloadgitlab-ce-37d6d1e46130f44f2fe05171b814b5682696839c.tar.gz
basic components
-rw-r--r--app/models/ci/scheduled_trigger.rb10
-rw-r--r--app/services/ci/create_pipeline_service.rb10
-rw-r--r--app/workers/scheduled_trigger_worker.rb8
-rw-r--r--spec/factories/ci/scheduled_triggers.rb18
-rw-r--r--spec/lib/ci/cron_parser_spec.rb91
-rw-r--r--spec/models/ci/scheduled_trigger_spec.rb31
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb4
-rw-r--r--spec/workers/scheduled_trigger_worker_spec.rb54
8 files changed, 127 insertions, 99 deletions
diff --git a/app/models/ci/scheduled_trigger.rb b/app/models/ci/scheduled_trigger.rb
index 5b1ff7bd7a4..9af274243a5 100644
--- a/app/models/ci/scheduled_trigger.rb
+++ b/app/models/ci/scheduled_trigger.rb
@@ -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
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 38a85e9fc42..6e3880e1e63 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -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?
diff --git a/app/workers/scheduled_trigger_worker.rb b/app/workers/scheduled_trigger_worker.rb
index 7dc17aa4332..5c2f03dee79 100644
--- a/app/workers/scheduled_trigger_worker.rb
+++ b/app/workers/scheduled_trigger_worker.rb
@@ -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
diff --git a/spec/factories/ci/scheduled_triggers.rb b/spec/factories/ci/scheduled_triggers.rb
index 9d45f4b4962..c97b2d14bd1 100644
--- a/spec/factories/ci/scheduled_triggers.rb
+++ b/spec/factories/ci/scheduled_triggers.rb
@@ -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
diff --git a/spec/lib/ci/cron_parser_spec.rb b/spec/lib/ci/cron_parser_spec.rb
index 58eb26c9421..f8c7e88edb3 100644
--- a/spec/lib/ci/cron_parser_spec.rb
+++ b/spec/lib/ci/cron_parser_spec.rb
@@ -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))
- 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))
+ 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 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
diff --git a/spec/models/ci/scheduled_trigger_spec.rb b/spec/models/ci/scheduled_trigger_spec.rb
index 68ba9c379b8..bb5e969fa44 100644
--- a/spec/models/ci/scheduled_trigger_spec.rb
+++ b/spec/models/ci/scheduled_trigger_spec.rb
@@ -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
+
+ describe '#update_last_run!' do
+ subject { scheduled_trigger.update_last_run! }
- context 'when cron and cron_time_zone are invaild' do
- it 'schedules nothing' do
+ let(:scheduled_trigger) { create(:ci_scheduled_trigger, :cron_nightly_build, last_run_at: nil) }
- end
+ it 'updates last_run_at' do
+ is_expected.not_to be_nil
end
end
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index d2f0337c260..4e34acc3585 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -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
diff --git a/spec/workers/scheduled_trigger_worker_spec.rb b/spec/workers/scheduled_trigger_worker_spec.rb
index c17536720a4..ffcb27602a1 100644
--- a/spec/workers/scheduled_trigger_worker_spec.rb
+++ b/spec/workers/scheduled_trigger_worker_spec.rb
@@ -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