diff --git a/lib/travis/api/app/endpoint/requests.rb b/lib/travis/api/app/endpoint/requests.rb index d8c05a5d..d8a18fdf 100644 --- a/lib/travis/api/app/endpoint/requests.rb +++ b/lib/travis/api/app/endpoint/requests.rb @@ -29,11 +29,14 @@ class Travis::Api::App # # I think we need to properly deprecate this by publishing a blog post. Metriks.meter("api.request.restart").mark + service = Travis::Enqueue::Services::RestartModel.new(current_user, params) + params[:user_id] = service.target.repository.owner.id - service = Travis::Enqueue::Services::RestartModel.new(current_user, { build_id: params[:build_id] }) - payload = {id: params[:build_id], user_id: current_user.id} - service.push("job:restart", payload) - status 202 + type = params[:build_id] ? 'build' : 'job' + params[:id] = params[:build_id] || params[:job_id] + + service.push("#{type}:restart", params) + respond_with(result: true, flash: service.messages) end end end diff --git a/lib/travis/api/v3/models/cron.rb b/lib/travis/api/v3/models/cron.rb index 68f664f2..e86527a9 100644 --- a/lib/travis/api/v3/models/cron.rb +++ b/lib/travis/api/v3/models/cron.rb @@ -13,7 +13,7 @@ module Travis::API::V3 elsif last_cron_build_date >= planned_time(LastBuild) planned_time(ThisBuild) else - Time.now + Time.now - 5.minutes end end diff --git a/lib/travis/api/v3/queries/crons.rb b/lib/travis/api/v3/queries/crons.rb index d5799d58..83a00245 100644 --- a/lib/travis/api/v3/queries/crons.rb +++ b/lib/travis/api/v3/queries/crons.rb @@ -7,10 +7,15 @@ module Travis::API::V3 def start_all() Models::Cron.all.select do |cron| - start(cron) if cron.next_enqueuing <= Time.now + begin + @cron = cron + start(cron) if cron.next_enqueuing <= Time.now + rescue => e + Raven.capture_exception(e, tags: { 'cron_id' => @cron.try(:id) }) + sleep(10) # This ensures the dyno does not spin down before the http request to send the error to sentry completes + next + end end - rescue => e - Raven.capture_exception(e) end def start(cron) @@ -21,7 +26,8 @@ module Travis::API::V3 return false end - user_id = branch.repository.users.detect { |u| u.github_oauth_token }.id + user_id = branch.repository.users.detect { |u| u.github_oauth_token }.try(:id) + user_id ||= branch.repository.owner.id payload = { repository: { id: branch.repository.github_id, owner_name: branch.repository.owner_name, name: branch.repository.name }, diff --git a/lib/travis/api/v3/service.rb b/lib/travis/api/v3/service.rb index f3243189..7a87f44c 100644 --- a/lib/travis/api/v3/service.rb +++ b/lib/travis/api/v3/service.rb @@ -68,7 +68,7 @@ module Travis::API::V3 def check_login_and_find(*args) raise LoginRequired unless access_control.full_access_or_logged_in? - find(*args) or raise NotFound + find(*args) or raise NotFound end def not_found(actually_not_found = false, type = nil) diff --git a/lib/travis/config/url.rb b/lib/travis/config/url.rb index 26e3cc6d..e44400a7 100644 --- a/lib/travis/config/url.rb +++ b/lib/travis/config/url.rb @@ -19,6 +19,8 @@ module Travis end end + Amqps = Amqp + class << self def parse(url) return Generic.new if url.nil? || url.empty? diff --git a/spec/integration/v2/requests_spec.rb b/spec/integration/v2/requests_spec.rb index de40b043..60c3eb86 100644 --- a/spec/integration/v2/requests_spec.rb +++ b/spec/integration/v2/requests_spec.rb @@ -30,7 +30,14 @@ describe 'Requests', set_app: true do describe 'POST /requests' do it 'triggers a build request using Hub' do response = post "/requests", { build_id: build.id }, headers - response.status.should be(202) + response.status.should be(200) end end + + it 'triggers a job request' do + payload = { job_id: build.matrix.first.id, user_id: repo.owner.id } + response = post "/requests", payload, headers + response.status.should be(200) + end + end diff --git a/spec/lib/travis/config_spec.rb b/spec/lib/travis/config_spec.rb new file mode 100644 index 00000000..d3f31d9c --- /dev/null +++ b/spec/lib/travis/config_spec.rb @@ -0,0 +1,163 @@ +require 'spec_helper' +require 'active_support/core_ext/hash/slice' + +describe Travis::Config do + let(:config) { Travis::Config.load(:files, :env, :heroku, :docker) } + + describe 'endpoints' do + it 'returns an object even without endpoints entry' do + config.endpoints.foo.should be_nil + end + + it 'returns endpoints if it is set' do + ENV['travis_config'] = YAML.dump('endpoints' => { 'ssh_key' => true }) + config.endpoints.ssh_key.should be_truthy + end + + it 'allows to set keys on enpoints when it is nil' do + config.endpoints.foo.should be_nil + + config.endpoints.foo = true + + config.endpoints.foo.should be_truthy + end + end + + describe 'defaults' do + it 'notifications defaults to []' do + config.notifications.should == [] + end + + it 'notifications.email defaults to {}' do + config.email.should == {} + end + + it 'queues defaults to []' do + config.queues.should == [] + end + + it 'ampq.host defaults to "localhost"' do + config.amqp.host.should == 'localhost' + end + + it 'ampq.prefetch defaults to 1' do + config.amqp.prefetch.should == 1 + end + + it 'queue.limit.by_owner defaults to {}' do + config.queue.limit.by_owner.should == {} + end + + it 'queue.limit.default defaults to 5' do + config.queue.limit.default.should == 5 + end + + it 'queue.interval defaults to 3' do + config.queue.interval.should == 3 + end + + it 'queue.interval defaults to 3' do + config.queue.interval.should == 3 + end + + it 'logs.shards defaults to 1' do + config.logs.shards.should == 1 + end + + it 'database' do + config.database.should == { + :adapter => 'postgresql', + :database => 'travis_test', + :encoding => 'unicode', + :min_messages => 'warning', + :variables => { :statement_timeout => 10000 } + } + end + end + + describe 'resource urls' do + describe 'with a TRAVIS_DATABASE_URL set' do + before { ENV['TRAVIS_DATABASE_URL'] = 'postgres://username:password@host:1234/database' } + after { ENV.delete('TRAVIS_DATABASE_URL') } + + it { config.database.username.should == 'username' } + it { config.database.password.should == 'password' } + it { config.database.host.should == 'host' } + it { config.database.port.should == 1234 } + it { config.database.database.should == 'database' } + it { config.database.encoding.should == 'unicode' } + end + + describe 'with a DATABASE_URL set' do + before { ENV['DATABASE_URL'] = 'postgres://username:password@host:1234/database' } + after { ENV.delete('DATABASE_URL') } + + it { config.database.username.should == 'username' } + it { config.database.password.should == 'password' } + it { config.database.host.should == 'host' } + it { config.database.port.should == 1234 } + it { config.database.database.should == 'database' } + it { config.database.encoding.should == 'unicode' } + end + + describe 'with a TRAVIS_LOGS_DATABASE_URL set' do + before { ENV['TRAVIS_LOGS_DATABASE_URL'] = 'postgres://username:password@host:1234/database' } + after { ENV.delete('TRAVIS_LOGS_DATABASE_URL') } + + it { config.logs_database.username.should == 'username' } + it { config.logs_database.password.should == 'password' } + it { config.logs_database.host.should == 'host' } + it { config.logs_database.port.should == 1234 } + it { config.logs_database.database.should == 'database' } + it { config.logs_database.encoding.should == 'unicode' } + end + + describe 'with a LOGS_DATABASE_URL set' do + before { ENV['LOGS_DATABASE_URL'] = 'postgres://username:password@host:1234/database' } + after { ENV.delete('LOGS_DATABASE_URL') } + + it { config.logs_database.username.should == 'username' } + it { config.logs_database.password.should == 'password' } + it { config.logs_database.host.should == 'host' } + it { config.logs_database.port.should == 1234 } + it { config.logs_database.database.should == 'database' } + it { config.logs_database.encoding.should == 'unicode' } + end + + describe 'with a TRAVIS_RABBITMQ_URL set' do + before { ENV['TRAVIS_RABBITMQ_URL'] = 'amqp://username:password@host:1234/vhost' } + after { ENV.delete('TRAVIS_RABBITMQ_URL') } + + it { config.amqp.username.should == 'username' } + it { config.amqp.password.should == 'password' } + it { config.amqp.host.should == 'host' } + it { config.amqp.port.should == 1234 } + it { config.amqp.vhost.should == 'vhost' } + end + + describe 'with a RABBITMQ_URL set' do + before { ENV['RABBITMQ_URL'] = 'amqp://username:password@host:1234/vhost' } + after { ENV.delete('RABBITMQ_URL') } + + it { config.amqp.username.should == 'username' } + it { config.amqp.password.should == 'password' } + it { config.amqp.host.should == 'host' } + it { config.amqp.port.should == 1234 } + it { config.amqp.vhost.should == 'vhost' } + end + + describe 'with a TRAVIS_REDIS_URL set' do + before { ENV['TRAVIS_REDIS_URL'] = 'redis://username:password@host:1234' } + after { ENV.delete('TRAVIS_REDIS_URL') } + + it { config.redis.url.should == 'redis://username:password@host:1234' } + end + + describe 'with a REDIS_URL set' do + before { ENV['REDIS_URL'] = 'redis://username:password@host:1234' } + after { ENV.delete('REDIS_URL') } + + it { config.redis.url.should == 'redis://username:password@host:1234' } + end + end +end diff --git a/spec/v3/models/cron_spec.rb b/spec/v3/models/cron_spec.rb index 9e323e66..7b9dd93a 100644 --- a/spec/v3/models/cron_spec.rb +++ b/spec/v3/models/cron_spec.rb @@ -43,11 +43,13 @@ describe Travis::API::V3::Models::Cron do describe "push build is ignored if disable by build is false" do before do + Timecop.return Timecop.travel(DateTime.new(2015, 12, 31, 16)) end after do Timecop.return + Timecop.freeze(Time.now.utc) end it "for daily builds" do @@ -85,11 +87,13 @@ describe Travis::API::V3::Models::Cron do describe "disable by build works with build" do before do + Timecop.return Timecop.travel(DateTime.new(2015, 12, 31, 16)) end after do Timecop.return + Timecop.freeze(Time.now.utc) end it "for daily builds" do @@ -121,11 +125,13 @@ describe Travis::API::V3::Models::Cron do describe "disable by build works without build" do before do + Timecop.return Timecop.travel(DateTime.new(2015, 12, 31, 16)) end after do Timecop.return + Timecop.freeze(Time.now.utc) end it "for daily builds" do @@ -157,12 +163,12 @@ describe Travis::API::V3::Models::Cron do describe "build starts now if next build time is in the past" do before do - # nothing, this time - # time freeze is performed in examples + Timecop.return end after do Timecop.return + Timecop.freeze(Time.now.utc) end it "for daily builds with disable_by_build true" do @@ -170,7 +176,7 @@ describe Travis::API::V3::Models::Cron do cron = Travis::API::V3::Models::Cron.create(branch_id: branch.id, interval: 'daily', disable_by_build: true) build = Travis::API::V3::Models::Build.create(:repository_id => repo.id, :branch_name => branch.name, :event_type => 'cron') Timecop.freeze(DateTime.new(2016, 1, 1, 19)) - expect(cron.next_enqueuing).to be == DateTime.now + expect(cron.next_enqueuing).to be == DateTime.now - 5.minutes build.destroy cron.destroy end @@ -180,7 +186,7 @@ describe Travis::API::V3::Models::Cron do cron = Travis::API::V3::Models::Cron.create(branch_id: branch.id, interval: 'daily', disable_by_build: false) build = Travis::API::V3::Models::Build.create(:repository_id => repo.id, :branch_name => branch.name, :event_type => 'cron') Timecop.freeze(DateTime.new(2016, 1, 1, 19)) - expect(cron.next_enqueuing).to be == DateTime.now + expect(cron.next_enqueuing).to be == DateTime.now - 5.minutes build.destroy cron.destroy end @@ -190,7 +196,7 @@ describe Travis::API::V3::Models::Cron do cron = Travis::API::V3::Models::Cron.create(branch_id: branch.id, interval: 'weekly', disable_by_build: true) build = Travis::API::V3::Models::Build.create(:repository_id => repo.id, :branch_name => branch.name, :event_type => 'cron') Timecop.freeze(DateTime.new(2016, 1, 7, 19)) - expect(cron.next_enqueuing).to be == DateTime.now + expect(cron.next_enqueuing).to be == DateTime.now - 5.minutes build.destroy cron.destroy end @@ -200,7 +206,7 @@ describe Travis::API::V3::Models::Cron do cron = Travis::API::V3::Models::Cron.create(branch_id: branch.id, interval: 'weekly', disable_by_build: false) build = Travis::API::V3::Models::Build.create(:repository_id => repo.id, :branch_name => branch.name, :event_type => 'cron') Timecop.freeze(DateTime.new(2016, 1, 7, 19)) - expect(cron.next_enqueuing).to be == DateTime.now + expect(cron.next_enqueuing).to be == DateTime.now - 5.minutes build.destroy cron.destroy end @@ -210,7 +216,7 @@ describe Travis::API::V3::Models::Cron do cron = Travis::API::V3::Models::Cron.create(branch_id: branch.id, interval: 'monthly', disable_by_build: true) build = Travis::API::V3::Models::Build.create(:repository_id => repo.id, :branch_name => branch.name, :event_type => 'cron') Timecop.freeze(DateTime.new(2016, 1, 31, 19)) - expect(cron.next_enqueuing).to be == DateTime.now + expect(cron.next_enqueuing).to be == DateTime.now - 5.minutes build.destroy cron.destroy end @@ -220,7 +226,7 @@ describe Travis::API::V3::Models::Cron do cron = Travis::API::V3::Models::Cron.create(branch_id: branch.id, interval: 'monthly', disable_by_build: false) build = Travis::API::V3::Models::Build.create(:repository_id => repo.id, :branch_name => branch.name, :event_type => 'cron') Timecop.freeze(DateTime.new(2016, 1, 31, 19)) - expect(cron.next_enqueuing).to be == DateTime.now + expect(cron.next_enqueuing).to be == DateTime.now - 5.minutes build.destroy cron.destroy end diff --git a/spec/v3/queries/cron_spec.rb b/spec/v3/queries/cron_spec.rb index d6d16889..1418a6e0 100644 --- a/spec/v3/queries/cron_spec.rb +++ b/spec/v3/queries/cron_spec.rb @@ -4,6 +4,7 @@ describe Travis::API::V3::Queries::Crons do let(:user) { Travis::API::V3::Models::User.find_by_login('svenfuchs') } let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } let(:existing_branch) { Travis::API::V3::Models::Branch.create(repository: repo, name: 'cron-test-existing', exists_on_github: true) } + let(:existing_branch2) { Travis::API::V3::Models::Branch.create(repository: repo, name: 'cron-test-existing2', exists_on_github: true) } let(:non_existing_branch) { Travis::API::V3::Models::Branch.create(repository: repo, name: 'cron-test-non-existing', exists_on_github: false) } let(:query) { Travis::API::V3::Queries::Crons.new({}, 'Overview') } @@ -25,8 +26,22 @@ describe Travis::API::V3::Queries::Crons do it 'enques error into a thread' do cron = Travis::API::V3::Models::Cron.create(branch_id: existing_branch.id, interval: 'daily', disable_by_build: false) error = StandardError.new('Konstantin broke all the thingz!') + Travis::API::V3::Queries::Crons.any_instance.expects(:sleep).with(10) Travis::API::V3::Models::Cron.any_instance.stubs(:branch).raises(error) - Raven.expects(:capture_exception).with(error) + Raven.expects(:capture_exception).with(error, tags: {'cron_id' => cron.id }) + query.start_all + end + + it 'continues running crons if one breaks' do + cron = Travis::API::V3::Models::Cron.create(branch_id: existing_branch.id, interval: 'daily', disable_by_build: false) + cron2 = Travis::API::V3::Models::Cron.create(branch_id: existing_branch2.id, interval: 'daily', disable_by_build: false) + + error = StandardError.new('Konstantin broke all the thingz!') + Travis::API::V3::Models::Cron.any_instance.stubs(:branch).raises(error) + + Travis::API::V3::Queries::Crons.any_instance.expects(:sleep).twice.with(10) + Raven.expects(:capture_exception).with(error, tags: {'cron_id' => cron.id }) + Raven.expects(:capture_exception).with(error, tags: {'cron_id' => cron2.id }) query.start_all end end