diff --git a/.gitignore b/.gitignore index b977aee1..e09d792f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ config/travis.yml +.yardoc diff --git a/Gemfile b/Gemfile index 86b97759..5d552334 100644 --- a/Gemfile +++ b/Gemfile @@ -3,10 +3,11 @@ ruby '1.9.3' rescue nil source :rubygems gemspec -gem 'puma' +gem 'unicorn' gem 'travis-support', github: 'travis-ci/travis-support' -gem 'travis-core', github: 'travis-ci/travis-core', branch: 'sf-travis-api' -gem 'hubble', github: 'roidrage/hubble' +gem 'travis-core', github: 'travis-ci/travis-core' +gem 'travis-sidekiqs', github: 'travis-ci/travis-sidekiqs', require: nil +gem "sentry-raven", github: 'getsentry/raven-ruby' gem 'yard-sinatra', github: 'rkh/yard-sinatra' gem 'rack-contrib', github: 'rack/rack-contrib' gem 'rack-cache', '~> 1.2' diff --git a/Gemfile.lock b/Gemfile.lock index f6d754b1..055ffb2d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,16 @@ GIT specs: micro_migrations (0.0.1) +GIT + remote: git://github.com/getsentry/raven-ruby.git + revision: 5d0e0eacbee39744bdf5b775efb3734d5b4361c7 + specs: + sentry-raven (0.3) + faraday (~> 0.8.0.rc2) + hashie + multi_json (~> 1.0) + uuidtools + GIT remote: git://github.com/rack/rack-contrib.git revision: b7e7c38fd02c3b5da91aa57af78b3f571c6ebcd0 @@ -30,18 +40,9 @@ GIT yard-sinatra (1.0.0) yard (~> 0.7) -GIT - remote: git://github.com/roidrage/hubble.git - revision: f5e6301ac24eabeebaf8f4485d71cdcf93b2f3f8 - specs: - hubble (0.1.2) - faraday - json (~> 1.6) - GIT remote: git://github.com/travis-ci/travis-core.git - revision: a55d84819e2f655c7b56472bc62c5037c94bbfc4 - branch: sf-travis-api + revision: 23c0c1b07f34033e598f02bfd03ec99b01198fce specs: travis-core (0.0.1) actionmailer (~> 3.2.3) @@ -60,6 +61,14 @@ GIT simple_states (~> 0.1.1) thor (~> 0.14.6) +GIT + remote: git://github.com/travis-ci/travis-sidekiqs.git + revision: 24d6afcc366979de91ee5bf2fbb4b628fc1a3d7a + specs: + travis-sidekiqs (0.0.1) + sentry-raven + sidekiq (~> 2.5.0) + GIT remote: git://github.com/travis-ci/travis-support.git revision: f78d9161369f574f12b9c0c0dbfe13b997766bdd @@ -114,10 +123,14 @@ GEM arel (3.0.2) atomic (1.0.1) avl_tree (1.1.3) - backports (2.6.4) + backports (2.6.5) builder (3.0.4) bunny (0.8.0) + celluloid (0.12.3) + facter (>= 1.6.12) + timers (>= 1.0.0) coderay (1.0.8) + connection_pool (0.9.2) daemons (1.1.9) dalli (2.3.0) data_migrations (0.0.1) @@ -127,18 +140,23 @@ GEM diff-lcs (1.1.3) erubis (2.7.0) eventmachine (1.0.0) + facter (1.6.14) factory_girl (2.4.2) activesupport faraday (0.8.4) multipart-post (~> 1.1) foreman (0.60.2) thor (>= 0.13.6) + hashie (1.2.0) hashr (0.0.22) hike (1.2.1) hitimes (1.1.1) + hubble (0.1.2) + yajl-ruby (~> 1.1) i18n (0.6.1) journey (1.0.4) json (1.7.5) + kgio (2.7.4) listen (0.5.3) mail (2.4.4) i18n (>= 0.4.0) @@ -153,14 +171,14 @@ GEM mime-types (1.19) mocha (0.12.7) metaclass (~> 0.0.1) - multi_json (1.3.6) + multi_json (1.3.7) multipart-post (1.1.5) net-http-persistent (2.8) net-http-pipeline (1.0.1) newrelic_rpm (3.3.5) pg (0.13.2) polyglot (0.3.3) - postmark (0.9.13) + postmark (0.9.15) json rake postmark-rails (0.4.1) @@ -171,8 +189,6 @@ GEM coderay (~> 1.0.5) method_source (~> 0.8) slop (~> 3.3.1) - puma (1.6.3) - rack (~> 1.2) pusher (0.9.4) multi_json (~> 1.0) signature (~> 0.1.2) @@ -192,11 +208,14 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) + raindrops (0.10.0) rake (0.9.2.2) rdoc (3.12) json (~> 1.4) redcarpet (2.2.2) redis (3.0.2) + redis-namespace (1.2.1) + redis (~> 3.0.0) rerun (0.7.1) listen rollout (1.1.0) @@ -208,6 +227,12 @@ GEM rspec-expectations (2.11.3) diff-lcs (~> 1.1.3) rspec-mocks (2.11.3) + sidekiq (2.5.2) + celluloid (~> 0.12.0) + connection_pool (~> 0.9.2) + multi_json (~> 1) + redis (~> 3) + redis-namespace signature (0.1.4) simple_states (0.1.1) activesupport @@ -234,10 +259,17 @@ GEM rack (>= 1.0.0) thor (0.14.6) tilt (1.3.3) - treetop (1.4.11) + timers (1.0.1) + treetop (1.4.12) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.33) + tzinfo (0.3.35) + unicorn (4.4.0) + kgio (~> 2.6) + rack + raindrops (~> 0.7) + uuidtools (2.1.3) + yajl-ruby (1.1.0) yard (0.8.3) PLATFORMS @@ -250,17 +282,18 @@ DEPENDENCIES factory_girl (~> 2.4.0) foreman gh! - hubble! micro_migrations! mocha (~> 0.12) pry - puma rack-cache (~> 1.2) rack-contrib! rake (~> 0.9.2) rerun rspec (~> 2.11) + sentry-raven! travis-api! travis-core! + travis-sidekiqs! travis-support! + unicorn yard-sinatra! diff --git a/config/unicorn.rb b/config/unicorn.rb new file mode 100644 index 00000000..2c751a22 --- /dev/null +++ b/config/unicorn.rb @@ -0,0 +1,23 @@ +# http://michaelvanrooijen.com/articles/2011/06/01-more-concurrency-on-a-single-heroku-dyno-with-the-new-celadon-cedar-stack/ + +worker_processes 3 # amount of unicorn workers to spin up +timeout 30 # restarts workers that hang for 15 seconds + +preload_app true + +before_fork do |server, worker| + ActiveRecord::Base.connection.disconnect! if defined?(ActiveRecord::Base) +end + +after_fork do |server, worker| + require 'travis' + + Travis::Amqp.connect + + ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base) + + if $metriks_reporter + $metriks_reporter.stop + $metriks_reporter.start + end +end diff --git a/lib/travis/api/app.rb b/lib/travis/api/app.rb index 34e458a3..e50c7d94 100644 --- a/lib/travis/api/app.rb +++ b/lib/travis/api/app.rb @@ -7,8 +7,8 @@ require 'rack/cache' require 'active_record' require 'redis' require 'gh' -require 'hubble' -require 'hubble/middleware' +require 'raven' +require 'sidekiq' # Rack class implementing the HTTP API. # Instances respond to #call. @@ -25,6 +25,7 @@ module Travis::Api autoload :Helpers, 'travis/api/app/helpers' autoload :Middleware, 'travis/api/app/middleware' autoload :Responders, 'travis/api/app/responders' + autoload :Cors, 'travis/api/app/cors' Rack.autoload :SSL, 'rack/ssl' @@ -52,9 +53,11 @@ module Travis::Api def initialize @app = Rack::Builder.app do - use Hubble::Rescuer, env: Travis.env, codename: ENV['CODENAME'] if Endpoint.production? && ENV['HUBBLE_ENDPOINT'] + use Travis::Api::App::Cors + use Raven::Rack if Endpoint.production? use Rack::Protection::PathTraversal use Rack::SSL if Endpoint.production? + use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache if memcache_servers = ENV['MEMCACHE_SERVERS'] @@ -67,7 +70,6 @@ module Travis::Api use Rack::Deflater use Rack::PostBodyContentTypeParser use Rack::JSONP - use ActiveRecord::ConnectionAdapters::ConnectionManagement use Rack::Config do |env| env['travis.global_prefix'] = env['SCRIPT_NAME'] @@ -81,6 +83,12 @@ module Travis::Api # Rack protocol def call(env) app.call(env) + rescue + if Endpoint.production? + [500, {'Content-Type' => 'text/plain'}, ['Travis encountered an error, sorry :(']] + else + raise + end end private @@ -96,6 +104,14 @@ module Travis::Api Travis::Amqp.config = Travis.config.amqp Travis::Database.connect Travis.services = Travis::Services + Travis::Features.start + Sidekiq.configure_client do |config| + config.redis = Travis.config.redis.merge(size: 1, namespace: Travis.config.sidekiq.namespace) + end + + Raven.configure do |config| + config.dsn = Travis.config.sentry.dsn + end if Travis.config.sentry end def self.load_endpoints diff --git a/lib/travis/api/app/cors.rb b/lib/travis/api/app/cors.rb new file mode 100644 index 00000000..781efc00 --- /dev/null +++ b/lib/travis/api/app/cors.rb @@ -0,0 +1,20 @@ +require 'travis/api/app' + +class Travis::Api::App + # Implements Cross-Origin Resource Sharing. Supported by all major browsers. + # See http://www.w3.org/TR/cors/ + # + # TODO: Be smarter about origin. + class Cors < Base + before do + headers['Access-Control-Allow-Origin'] = "*" + headers['Access-Control-Allow-Credentials'] = "true" + headers['Access-Control-Expose-Headers'] = "Content-Type, Cache-Control, Expires, Etag, Last-Modified" + end + + options // do + headers['Access-Control-Allow-Methods'] = "HEAD, GET, POST, PATCH, PUT, DELETE" + headers['Access-Control-Allow-Headers'] = "Content-Type, Authorization, Accept, If-None-Match, If-Modified-Since" + end + end +end diff --git a/lib/travis/api/app/middleware/cors.rb b/lib/travis/api/app/middleware/cors.rb deleted file mode 100644 index 5a93b7a3..00000000 --- a/lib/travis/api/app/middleware/cors.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'travis/api/app' - -class Travis::Api::App - class Middleware - # Implements Cross-Origin Resource Sharing. Supported by all major browsers. - # See http://www.w3.org/TR/cors/ - # - # TODO: Be smarter about origin. - class Cors < Middleware - before do - headers['Access-Control-Allow-Origin'] = "*" - headers['Access-Control-Allow-Credentials'] = "true" - headers['Access-Control-Expose-Headers'] = "Content-Type, Cache-Control, Expires, Etag, Last-Modified" - end - - options // do - headers['Access-Control-Allow-Methods'] = "HEAD, GET, POST, PATCH, PUT, DELETE" - headers['Access-Control-Allow-Headers'] = "Content-Type, Authorization, Accept, If-None-Match, If-Modified-Since" - end - end - end -end diff --git a/lib/travis/api/app/responders/service.rb b/lib/travis/api/app/responders/service.rb index 5af8b8fe..bc775195 100644 --- a/lib/travis/api/app/responders/service.rb +++ b/lib/travis/api/app/responders/service.rb @@ -16,7 +16,7 @@ module Travis::Api::App::Responders def cache_control if final? mode = endpoint.public? ? :public : :private - endpoint.expires(31536000, mode, :must_revalidate) # 1 year + endpoint.expires(31536000, mode) # 1 year else # FIXME: Chrome WTF? endpoint.cache_control :no_cache diff --git a/script/server b/script/server index a17c4245..29326751 100755 --- a/script/server +++ b/script/server @@ -3,6 +3,6 @@ cd "$(dirname "$0")/.." [ $PORT ] || PORT=3000 [ $RACK_ENV ] || RACK_ENV=development -cmd="ruby -I lib -S bundle exec ruby -I lib -S puma config.ru -p $PORT -e $RACK_ENV --threads 0:16" -[[ $RACK_ENV == "development" ]] && exec rerun "$cmd -b tcp://127.0.0.1:$PORT" +cmd="ruby -I lib -S bundle exec ruby -I lib -S unicorn config.ru -p $PORT -E $RACK_ENV" +[[ $RACK_ENV == "development" ]] && exec rerun "$cmd -l 127.0.0.1:$PORT" exec $cmd diff --git a/spec/integration/v2/hooks_spec.rb b/spec/integration/v2/hooks_spec.rb index 6e6b8fb6..03e61a93 100644 --- a/spec/integration/v2/hooks_spec.rb +++ b/spec/integration/v2/hooks_spec.rb @@ -37,8 +37,9 @@ describe 'Hooks' do it 'sets the hook' do GH.stubs(:[]).returns([]) GH.expects(:post).with(target, payload).returns(GH.load(PAYLOADS[:github][:hook_active])) - put 'hooks', { hook: { id: hook.id, active: 'true' } }, headers + response = put 'hooks', { hook: { id: hook.id, active: 'true' } }, headers repo.reload.active?.should be_true + response.should be_successful end end end diff --git a/spec/unit/middleware/cors_spec.rb b/spec/unit/cors_spec.rb similarity index 93% rename from spec/unit/middleware/cors_spec.rb rename to spec/unit/cors_spec.rb index 16d565bd..83e79914 100644 --- a/spec/unit/middleware/cors_spec.rb +++ b/spec/unit/cors_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Travis::Api::App::Middleware::Cors do +describe Travis::Api::App::Cors do before do mock_app do - use Travis::Api::App::Middleware::Cors + use Travis::Api::App::Cors get('/check_cors') { 'ok' } end end diff --git a/spec/unit/responders/service_spec.rb b/spec/unit/responders/service_spec.rb new file mode 100644 index 00000000..5ee9412f --- /dev/null +++ b/spec/unit/responders/service_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Travis::Api::App::Responders::Service do + class MyService < Travis::Api::App::Responders::Service + end + + let(:endpoint) { stub 'endpoint', public?: true } + let(:resource) { stub 'resource', run: {} } + let(:options) { {} } + let(:service) { MyService.new(endpoint, resource, options) } + + context 'with final resource' do + before { resource.expects(:final?).returns(true) } + + it 'caches resource for a year' do + endpoint.expects(:expires).with(31536000, :public) + service.apply + end + end + +end