diff --git a/.editorconfig b/.editorconfig index 99580d0..c3895b4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,7 @@ insert_final_newline = true indent_style = space indent_size = 2 trim_trailing_whitespace = true + +[alpine/**] +indent_size = 4 +indent_style = tab diff --git a/.env.sample b/.env.sample index f2fd0c9..49ec8fe 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,6 @@ RACK_ENV=development TZ=Europe/Prague RAISE_ERRORS=true FORCE_SSL=false -TIMEOUT=false +RACK_TIMEOUT=false DOMAIN=example.com SYNC_SCHEDULE=0 */12 * * * diff --git a/.travis.yml b/.travis.yml index 32f8c23..d2d34af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,49 +1,86 @@ -language: ruby sudo: false dist: trusty -addons: - postgresql: '9.5' - apt: - sources: - - elasticsearch-1.7 - packages: - - elasticsearch - code_climate: - repo_token: a813ce884be14a158262d4e05906c0b6f4e7f4533c41cdf88bda7da84d88297f -cache: - bundler: true -rvm: - - 2.3 - - 2.4 - - ruby-head -services: - - postgresql - - elasticsearch -bundler_args: --without development doc -before_script: - - while read line; do export $line; done < .env.test - - bundle exec rake --trace db:setup - - echo 'waiting for elasticsearch to come up'; wget -q --waitretry=1 --retry-connrefused -T 10 -O - http://127.0.0.1:9200 -after_success: | - if [ "$TRAVIS_BRANCH" = master ]; then - nvm install 4.2 && nvm use 4.2 \ - && npm install -g raml2html \ - && bundle exec rake raml:publish - fi -notifications: - email: - on_success: never # default: change - on_failure: change # default: always - webhooks: - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always env: global: - - secure: RyyZpzIhIJVX+1CKn5BKx3siHeDwUux+q4/dMrmc0P06aCHvSapdrUjPOWK1bL5CpqNrybvPOYTpxc0JHIIkgzIAGJJEBaBHvlwqj0ZiH8P3hj0TpCZcfWROnRSpzazLwN0isdRxVHGYRIDGJBDZoN0PX97anjmclBt+UveLXz4= # GH_TOKEN - - secure: Xj2KE+N9NOwwMa8MwAqpCKB7ujhEwR69YrxlskRDcjcoCqDWtRQPfDbPr1X0Nb3bb5MmS1ouoQt6ACccRuJWNQGg/8jI9yaLu4UIroNyv86q0uduhFW4fG0IEjk4W7+wk6o89Q03XbXpp7tvs6t56Ned1ClZOBhZOGl4jtPgJyY= # OAUTH_CLIENT_ID - - secure: dhn9CHHyKIRPYSL0t3qPIcQ2GEI9IhSFAPG/JKy4rL1RIOzlxiFYVz9juaDIjGw8Ab7+Pb+tL0zurE76eN3vqx4d6Gw+rDrTl46mbtyqs6MZYSwFMLqqRb3k2m1Y6WKNoB01GVMB2H7AmB0YMUzhaM4WF3h0XWNPRczw3U2X/eI= # OAUTH_CLIENT_SECRET -matrix: + - secure: RyyZpzIhIJVX+1CKn5BKx3siHeDwUux+q4/dMrmc0P06aCHvSapdrUjPOWK1bL5CpqNrybvPOYTpxc0JHIIkgzIAGJJEBaBHvlwqj0ZiH8P3hj0TpCZcfWROnRSpzazLwN0isdRxVHGYRIDGJBDZoN0PX97anjmclBt+UveLXz4= # GH_TOKEN + - secure: Xj2KE+N9NOwwMa8MwAqpCKB7ujhEwR69YrxlskRDcjcoCqDWtRQPfDbPr1X0Nb3bb5MmS1ouoQt6ACccRuJWNQGg/8jI9yaLu4UIroNyv86q0uduhFW4fG0IEjk4W7+wk6o89Q03XbXpp7tvs6t56Ned1ClZOBhZOGl4jtPgJyY= # OAUTH_CLIENT_ID + - secure: dhn9CHHyKIRPYSL0t3qPIcQ2GEI9IhSFAPG/JKy4rL1RIOzlxiFYVz9juaDIjGw8Ab7+Pb+tL0zurE76eN3vqx4d6Gw+rDrTl46mbtyqs6MZYSwFMLqqRb3k2m1Y6WKNoB01GVMB2H7AmB0YMUzhaM4WF3h0XWNPRczw3U2X/eI= # OAUTH_CLIENT_SECRET + +jobs: + include: + - &test + stage: test + language: ruby + rvm: 2.3 + addons: + postgresql: '9.5' + code_climate: + repo_token: a813ce884be14a158262d4e05906c0b6f4e7f4533c41cdf88bda7da84d88297f + services: + - postgresql + cache: + bundler: true + install: + # Travis provides only ElasticSearch 5.x, we must install legacy version ourself. + - "wget -nv https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.3.tar.gz + && echo '754b089ec0a1aae5b36b39391d5385ed7428d8f5 elasticsearch-1.7.3.tar.gz' | sha1sum -c + && tar -xzf elasticsearch-*.tar.gz" + - bundle install --without development doc --jobs=3 --retry=3 + before_script: + - while read line; do export $line; done < .env.test + - bundle exec rake --trace db:setup + - ./elasticsearch-*/bin/elasticsearch & + - echo 'waiting for elasticsearch to come up'; wget -q --waitretry=1 --retry-connrefused -T 10 -O - http://127.0.0.1:9200 + + - <<: *test + rvm: 2.4 + + - &test-ruby-head + <<: *test + rvm: ruby-head + + - stage: deploy + language: node_js + node_js: 4 + rvm: 2.4 + cache: + bundler: true + install: + - npm install -g raml2html + - bundle install --jobs=3 --retry=3 + script: skip + deploy: + - provider: script + script: bundle exec rake raml:publish + skip_cleanup: true + on: + branch: master + + # Build Alpine package. + - stage: deploy + sudo: true + language: minimal + before_install: + - "wget -nv https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.6.0/alpine-chroot-install \ + && echo 'a827a4ba3d0817e7c88bae17fe34e50204983d1e alpine-chroot-install' | sha1sum -c" + - alpine() { /alpine/enter-chroot -u "$USER" "$@"; } + install: + - sudo sh alpine-chroot-install -b v3.6 -p 'build-base alpine-sdk' + before_script: + - /alpine/enter-chroot sh -c "addgroup $USER wheel && addgroup $USER abuild" + - alpine abuild-keygen -ain + script: + - cd alpine && alpine abuild -R + fast_finish: true + # This doesn't work now, probably bug in Travis. allow_failures: - - rvm: ruby-head - - rvm: 2.4 + - <<: *test-ruby-head + +notifications: + email: + on_success: never + on_failure: change + webhooks: + on_success: change + on_failure: always diff --git a/Rakefile b/Rakefile index 4d1d776..76ce904 100644 --- a/Rakefile +++ b/Rakefile @@ -3,6 +3,3 @@ require 'pliny/tasks' Dir['./lib/tasks/*.rake'].each { |task| load task } task :default => :spec - - - diff --git a/alpine/APKBUILD b/alpine/APKBUILD new file mode 100644 index 0000000..1c05d25 --- /dev/null +++ b/alpine/APKBUILD @@ -0,0 +1,74 @@ +# Maintainer: Jakub Jirutka +pkgname=sirius +pkgver=${PKGVER:-$(date +%Y%m%d)} +pkgrel=0 +pkgdesc="CTU Time management API" +url="https://github.com/cvut/sirius" +arch="all" +license="MIT" +depends="ruby-bigdecimal ruby-io-console ruby-irb ruby-json ruby-rake + ruby-bundler ca-certificates tzdata" +makedepends="ruby-dev postgresql-dev" +pkgusers="sirius" +pkggroups="sirius" +install="$pkgname.pre-install" +source="" +builddir="$startdir/.." + +_prefix="usr/bundles/$pkgname" + +build() { + cd "$builddir" + + CI=true bundle install \ + --deployment \ + --frozen \ + --jobs=${JOBS:-2} \ + --no-cache \ + --without development test + + rm -rf vendor/bundle/ruby/*/cache +} + +package() { + local destdir="$pkgdir/$_prefix" + + # Install files + + cd "$builddir" + + mkdir -p "$destdir" + cp -ar app config db lib vendor config.ru Gemfile* Rakefile "$destdir"/ + + install -m 755 -D "$startdir"/$pkgname.initd "$pkgdir"/etc/init.d/$pkgname + install -m 755 -D "$startdir"/$pkgname.scheduler.initd "$pkgdir"/etc/init.d/$pkgname.scheduler + install -m 644 -D "$startdir"/$pkgname.confd "$pkgdir"/etc/conf.d/$pkgname + install -m 644 -D "$startdir"/$pkgname.logrotate "$pkgdir"/etc/logrotate.d/$pkgname + + install -d -m 755 -o sirius -g sirius "$pkgdir"/var/log/$pkgname + + # Cleanup + + cd vendor/bundle/ruby/*/ + + # Remove documentations and other unused textual files. + find gems/ -name 'doc' -type d -exec rm -frv '{}' + + find gems/ \ + -name 'README*' -delete \ + -o -name 'CHANGELOG*' -delete \ + -o -name 'CONTRIBUT*' -delete \ + -o -name '*LICENSE*' -delete + + # Remove sources and binaries of native extensions (they are installed + # in extensions directory). + find gems/ -type d -name ext -maxdepth 2 -exec rm -frv '{}' + + find gems/ -name '*.so' -delete + + # Remove build logs and cache. + rm -rf cache + find extensions/ \ + -name gem_make.out -delete \ + -o -name mkmf.log -delete +} + +sha512sums="" diff --git a/alpine/sirius.confd b/alpine/sirius.confd new file mode 100644 index 0000000..528ce19 --- /dev/null +++ b/alpine/sirius.confd @@ -0,0 +1,42 @@ +# Configuration for /etc/init.d/sirius and /etc/init.d/sirius.scheduler + +# Domain name of the application (required). +domain= + +# URI of PostgreSQL database (e.g. postgres://sirius:top-secret@db.org/sirius) (required). +database_url= + +# URI of Elastic DB (e.g. http://elastic.example.org:9200) (required). +elastic_url= + +# OAuth 2.0 client_id used to access KOSapi (required). +oauth_client_id= + +# OAuth 2.0 client_secret used to access KOSapi (required). +oauth_client_secret= + + +# HTTP port to listen on (default: 5000). +#http_port= + +# Timeout (in seconds) for HTTP requests (default: 10). +# Requests which exceed this timeout are aborted. +#rack_timeout= + +# Number of Puma worker processes to launch (default: 3). +#puma_workers= + +# Minimum number of threads to by available in Puma's thread pool (default: 1). +#puma_min_threads= + +# Maximum number of threads to by available in Puma's thread pool (default: 16). +#puma_max_threads= + +# The maximum size of the connection pool (default: 3). +#db_pool_max_size= + +# Sentry DSN (optional). +#sentry_dsn= + +# Cron expression specifying when to run import and planning procedure (optional). +#sync_schedule= diff --git a/alpine/sirius.initd b/alpine/sirius.initd new file mode 100644 index 0000000..2a49166 --- /dev/null +++ b/alpine/sirius.initd @@ -0,0 +1,63 @@ +#!/sbin/openrc-run + +# XXX: We must disable globbing to prevent messing of SYNC_SCHEDULE variable! +set -f + +name="Sirius" + +extra_started_commands="reload" +description_reload="Perform a hot restart (keeps the server sockets open)." + +: ${user:="sirius"} +: ${basedir:="/usr/bundles/sirius"} +: ${logdir:="/var/log/sirius"} +: ${puma_socket:="/run/sirius/server.sock"} +: ${timezone:="Europe/Prague"} + +command="/usr/bin/bundle" +command_args="exec puma --config config/puma.rb config.ru" +command_background="yes" + +pidfile="/run/$RC_SVCNAME/server.pid" +start_stop_daemon_args=" + --chdir '$basedir' + --user $user + --stdout '$logdir/puma.log' + --stderr '$logdir/puma.log' + --wait 1000 + --env RACK_ENV=production + --env TZ=$timezone + $start_stop_daemon_args" + +depend() { + use net postgresql +} + +start_pre() { + checkpath -d -o $user -m755 "${pidfile%/*}" + checkpath -d -o $user -m755 "${puma_socket%/*}" + checkpath -d -o $user -m755 "$logdir" + + local name value + for name in database_url db_pool_max_size domain elastic_url http_port \ + oauth_client_id oauth_client_secret puma_max_threads puma_min_threads \ + puma_workers rack_timeout sentry_dsn sync_schedule + do + value=$(getval $name) + if [ -n "$value" ]; then + start_stop_daemon_args="$start_stop_daemon_args + --env $(echo $name | tr '[a-z]' '[A-Z]')='$value'" + fi + done +} + +reload() { + ebegin "Reloading $name" + kill -USR2 $(head -n1 $pidfile) + eend $? +} + +getval() { + local var_name="$1" + eval "printf '%s\n' \"\${$var_name:-}\"" +} diff --git a/alpine/sirius.logrotate b/alpine/sirius.logrotate new file mode 100644 index 0000000..1af8a8e --- /dev/null +++ b/alpine/sirius.logrotate @@ -0,0 +1,5 @@ +/var/log/sirius/*.log { + missingok + notifempty + copytruncate +} diff --git a/alpine/sirius.pre-install b/alpine/sirius.pre-install new file mode 100644 index 0000000..1e573e1 --- /dev/null +++ b/alpine/sirius.pre-install @@ -0,0 +1,6 @@ +#!/bin/sh + +addgroup -S sirius 2>/dev/null +adduser -S -D -H -h /usr/bundles/sirius -s /sbin/nologin -G sirius -g "added by apk for sirius" sirius 2>/dev/null + +exit 0 diff --git a/alpine/sirius.scheduler.initd b/alpine/sirius.scheduler.initd new file mode 100644 index 0000000..c55da6a --- /dev/null +++ b/alpine/sirius.scheduler.initd @@ -0,0 +1,52 @@ +#!/sbin/openrc-run + +# XXX: We must disable globbing to prevent messing of SYNC_SCHEDULE variable! +set -f + +name="Sirius (scheduler)" + +: ${user:="sirius"} +: ${basedir:="/usr/bundles/sirius"} +: ${logdir:="/var/log/sirius"} +: ${timezone:="Europe/Prague"} + +command="/usr/bin/bundle" +command_args="exec script/scheduler" +command_background="yes" + +pidfile="/run/${RC_SVCNAME%.scheduler}/scheduler.pid" +start_stop_daemon_args=" + --chdir '$basedir' + --user $user + --stdout '$logdir/scheduler.log' + --stderr '$logdir/scheduler.log' + --wait 1000 + --env RACK_ENV=production + --env TZ=$timezone + $start_stop_daemon_args" + +depend() { + use net postgresql +} + +start_pre() { + checkpath -d -o $user -m755 "${pidfile%/*}" + checkpath -d -o $user -m755 "$logdir" + + local name value + for name in database_url db_pool_max_size domain elastic_url http_port \ + oauth_client_id oauth_client_secret puma_max_threads puma_min_threads \ + puma_workers rack_timeout sentry_dsn sync_schedule + do + value=$(getval $name) + if [ -n "$value" ]; then + start_stop_daemon_args="$start_stop_daemon_args + --env $(echo $name | tr '[a-z]' '[A-Z]')='$value'" + fi + done +} + +getval() { + local var_name="$1" + eval "printf '%s\n' \"\${$var_name:-}\"" +} diff --git a/config/config.rb b/config/config.rb index 17e6d19..2b27da0 100644 --- a/config/config.rb +++ b/config/config.rb @@ -26,7 +26,7 @@ module Config # Override -- value is returned or the set default. override :database_timeout, 10, int - override :db_pool, 5, int + override :db_pool_max_size, 3, int override :deployment, 'production', string override :elastic_prefix, 'sirius', string override :force_ssl, true, bool @@ -34,14 +34,14 @@ module Config override :oauth_auth_uri, 'https://auth.fit.cvut.cz/oauth/authorize', string override :oauth_token_uri, 'https://auth.fit.cvut.cz/oauth/token', string override :umapi_people_uri, 'https://kosapi.fit.cvut.cz/usermap/v1/people', string - override :umapi_privileged_roles, 'B-00000-ZAMESTNANEC', array(string) + override :umapi_privileged_roles, 'B-00000-SUMA-ZAMESTNANEC', array(string) - override :port, 5000, int + override :http_port, 5000, int override :pretty_json, false, bool override :rack_env, 'development', string override :raise_errors, false, bool override :root, File.expand_path('../', __dir__), string - override :timeout, 10, int + override :rack_timeout, 10, int override :tz, 'Europe/Prague', string override :versioning, false, bool diff --git a/config/initializers/database.rb b/config/initializers/database.rb index 8e9f2c9..8ded67d 100644 --- a/config/initializers/database.rb +++ b/config/initializers/database.rb @@ -7,7 +7,7 @@ # while Sequel expects 'postgres' adapter db_url = Config.database_url.sub(/\Apostgresql:/, 'postgres:') -DB = Sequel.connect(db_url, max_connections: Config.db_pool) +DB = Sequel.connect(db_url, max_connections: Config.db_pool_max_size) # Log level at which to log SQL queries. DB.sql_log_level = :debug diff --git a/config/initializers/timeout.rb b/config/initializers/timeout.rb index 4f42b04..a27cd74 100644 --- a/config/initializers/timeout.rb +++ b/config/initializers/timeout.rb @@ -1,5 +1,7 @@ +require 'logger' require 'rack-timeout' -if Config.timeout > 0 - Rack::Timeout.timeout = Config.timeout +if Config.rack_timeout > 0 + Rack::Timeout.timeout = Config.rack_timeout + Rack::Timeout::Logger.level = Logger::WARN end diff --git a/config/puma.rb b/config/puma.rb index 45ecf10..779c5e7 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,6 +1,6 @@ require './config/config' environment Config.rack_env -port Config.port +port Config.http_port quiet threads Config.puma_min_threads.to_i, Config.puma_max_threads.to_i diff --git a/lib/routes.rb b/lib/routes.rb index 109eabc..b08e2b4 100644 --- a/lib/routes.rb +++ b/lib/routes.rb @@ -3,7 +3,7 @@ Routes = Rack::Builder.new do use Pliny::Middleware::RescueErrors, raise: Config.raise_errors? use Pliny::Middleware::CORS - use Rack::Timeout if Config.timeout > 0 + use Rack::Timeout if Config.rack_timeout > 0 use Raven::Rack if Config.sentry_dsn use Rack::Deflater use Rack::MethodOverride diff --git a/vagga.yaml b/vagga.yaml index 5016888..1a4e74a 100644 --- a/vagga.yaml +++ b/vagga.yaml @@ -96,4 +96,3 @@ commands: children: postgres: *postgres elastic: *elastic -