sinatra will not load config data when started via systemd - ruby

i have a sinatra app. when loaded locally via bundler, everything is fine. when i load the app via systemd the app got started, but the config file seems to be not loading. It says that in the logs that its loaded but the values are not available inside the app
The unit file:
[Unit]
Description=PNL: Puppet Node Lister
After=network.target
[Service]
Type=simple
ExecStart=/bin/bundle exec ruby puppetdb_node_lister.rb
KillMode=process
Environment=RACK_ENV="production"
Restart=always
User=pnl
Group=pnl
WorkingDirectory=/opt/puppetdb_node_lister
[Install]
WantedBy=multi-user.target
my sinatra app:
require 'rubygems'
require 'sinatra'
require 'sinatra/config_file'
require 'json'
require 'rest-client'
require 'date'
require 'time'
require 'active_support/time_with_zone'
require 'tilt/erb'
### local methods
require_relative 'lib/methods.rb'
config_file 'config.yaml'
### request area
get '/' do
"#{settings.methods(false).inspect} <br/><br/> #{$:} <br/><br/> #{File.expand_path(File.dirname(__FILE__))}"
end
my yaml:
development:
username: 'test'
password: 'test'
puppetdb: 'puppet.example.com'
puppetsrv: 'puppet.example.com'
production:
username: 'superadmin'
password: 'somes3cret'
puppetdb: 'puppet.example.com'
puppetsrv: 'puppet.example.com'
local bundle output of /
[:app_file=, :app_file, :app_file?, :logging=, :logging, :logging?, :method_override=, :method_override, :method_override?, :run=, :run, :run?, :session_secret=, :session_secret, :session_secret?, :register, :environments=, :environments, :environments?, :username=, :username, :username?, :password=, :password, :password?, :puppetdb=, :puppetdb, :puppetdb?, :puppetsrv=, :puppetsrv, :puppetsrv?, :traps=, :traps, :traps?, :running_server=, :running_server, :running_server?, :handler_name=, :handler_name, :handler_name?]
["/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/sinatra-contrib-1.4.7/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/sinatra-1.4.7/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/tilt-2.0.4/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rubygems-update-2.6.4/hide_lib_for_update", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rest-client-1.8.0/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rack-test-0.6.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rack-protection-1.5.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rack-1.6.4/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/netrc-0.11.0/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/multi_json-1.12.1/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/mime-types-2.99.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/http-cookie-1.0.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/domain_name-0.5.20160310/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/unf-0.1.4/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/unf_ext-0.0.7.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/unf_ext-0.0.7.2/lib", "/usr/share/gems/gems/bundler-1.7.8/lib/gems/bundler-1.7.8/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/backports-3.6.8/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/activesupport-4.2.6/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/tzinfo-1.2.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/thread_safe-0.3.5/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/minitest-5.9.0/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/json-1.8.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/json-1.8.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/i18n-0.7.0/lib", "/usr/share/gems/gems/bundler-1.7.8/lib", "/usr/local/share/ruby/site_ruby", "/usr/local/lib64/ruby/site_ruby", "/usr/share/ruby/vendor_ruby", "/usr/lib64/ruby/vendor_ruby", "/usr/share/rubygems", "/usr/share/ruby", "/usr/lib64/ruby/"]
/opt/puppetdb_node_lister
systemd output of /
[:app_file=, :app_file, :app_file?, :logging=, :logging, :logging?, :method_override=, :method_override, :method_override?, :run=, :run, :run?, :session_secret=, :session_secret, :session_secret?, :register, :environments=, :environments, :environments?, :traps=, :traps, :traps?, :running_server=, :running_server, :running_server?, :handler_name=, :handler_name, :handler_name?]
["/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/sinatra-contrib-1.4.7/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/sinatra-1.4.7/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/tilt-2.0.4/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rubygems-update-2.6.4/hide_lib_for_update", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rest-client-1.8.0/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rack-test-0.6.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rack-protection-1.5.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/rack-1.6.4/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/netrc-0.11.0/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/multi_json-1.12.1/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/mime-types-2.99.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/http-cookie-1.0.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/domain_name-0.5.20160310/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/unf-0.1.4/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/unf_ext-0.0.7.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/unf_ext-0.0.7.2/lib", "/usr/share/gems/gems/bundler-1.7.8/lib/gems/bundler-1.7.8/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/backports-3.6.8/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/activesupport-4.2.6/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/tzinfo-1.2.2/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/thread_safe-0.3.5/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/minitest-5.9.0/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/json-1.8.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/json-1.8.3/lib", "/opt/puppetdb_node_lister/vendor/bundle/ruby/gems/i18n-0.7.0/lib", "/usr/share/gems/gems/bundler-1.7.8/lib", "/usr/local/share/ruby/site_ruby", "/usr/local/lib64/ruby/site_ruby", "/usr/share/ruby/vendor_ruby", "/usr/lib64/ruby/vendor_ruby", "/usr/share/rubygems", "/usr/share/ruby", "/usr/lib64/ruby/"]
/opt/puppetdb_node_lister
one can see, that if loaded via systemd the values in settings (which would come from the yaml) are missing.
can anybody help me with that? am i doing this right, or is there i'm missing something?

after all it was
Environment=RACK_ENV="production"
which should be more like
Environment=RACK_ENV=production
after this change, everything worked as expected!

Your systemd configuration looks good to me. You set both the environment variables and the WorkingDirectory correctly.
Replace your app that with something that just dumps out the environment and the current directory. Run this test app through bundler, and also via systemd.
You should find there's still some difference between the environment that is being set up by systemd and your alternate method. Resolve the differences. :)

Related

How to pass Puma::Configuration to Sinatra?

This is my web app:
class Front < Sinatra::Base
configure do
set :server, :puma
end
get '/' do
'Hello, world!'
end
end
I start it like this (don't suggest to use Rack, please):
Front.start!
Here is my configuration object for Puma, which I don't know how to pass to it:
require 'puma/configuration'
Puma::Configuration.new({ log_requests: true, debug: true })
Seriously, how?
Configuration is tightly connected to a way in which you run puma server.
The standard way to run puma - puma CLI command. In order to configure puma config file config/puma.rb or config/puma/<environment>.rb should be provided (see example).
But you asked how to pass Puma::Configuration object to puma. I wonder why you need it but AFAIK you need to run puma server programmatically in your application code with Puma::Launcher(see source code)
conf = Puma::Configuration.new do |user_config|
user_config.threads 1, 10
user_config.app do |env|
[200, {}, ["hello world"]]
end
end
Puma::Launcher.new(conf, events: Puma::Events.stdio).run
user_config.app may be any callable object (compatible with Rack interface) like Sinatra application.
Hope it's helpful.
Do you want to pass exactly an object or just a configuration in general? For the last option it's possible, but Puma will not log anything anyway (I'm not sure, but seems like you worry exactly about logging settings for Puma).
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'bundler/inline'
gemfile(true) do
gem 'sinatra'
gem 'puma'
gem 'openssl'
end
require 'sinatra/base'
class Front < Sinatra::Base
configure do
set :server, :puma
set :server_settings, log_requests: true, debug: true, environment: 'foo'
end
get '/' do
'Hello, world!'
end
end
Front.start!

Sinatra Routing - Separate Files

I'm going through a recently released book on Sinatra that demonstrates this way of setting up routes in different files:
# app.rb
require "sinatra"
require "slim"
class Todo < Sinatra::Base
# ...
Dir[File.join(File.dirname(__FILE__), "lib", "*.rb")].each { |lib| require lib }
end
# lib/routes.rb
get "/test" do
"The application is running"
end
# config.ru
require "sinatra"
require "bundler/setup"
Bundler.require
ENV["RACK_ENV"] = "development"
require File.join(File.dirname(__FILE__), "app.rb")
Todo.start!
However, it fails to find the route at http://localhost:4567/test. It would make sense to me that this should work when I run ruby config.ru or bundle exec rackup -p 4567. But coming from Rails development where all this configuration is built-in, I don't have a complete understanding of how everything gets wired together. The server is running on that port and I get the Sinatra doesn't know this ditty 404 page. If I reopen the class as suggested by this SO answer, the /test route is found.
# lib/routes.rb
class Todo < Sinatra::Base
get "/test" do
"The application is running"
end
end
Is there something I'm missing about this suggested way to include routes without reopening the class?
Try ruby app.rb, it should work.
You'll need to restart the webserver to load routes that were added while it was running. Routes are loaded into memory when app.rb is invoked and Sinatra is launched. The route itself looks fine and it appears routes.rb is being imported successfully via Dir[File.join(File.dirname(__FILE__), "lib", "*.rb")].each { |lib| require lib }.
If you're running the server directly through terminal Ctrl+X, Ctrl+C should shut it down, then restart it via rackup config.ru* or ruby app.rb. You may confirm the route is recognized by making a get request through your browser to: http://127.0.0.1:4567/test.
For the rackup config.ru command to work, you can change config.ru to something like:
# config.ru
require './app'
run Sinatra::Application
This is just a deployment convenience.
Edit: #shaun, because Todo extends Sinatra::Base it's fine to use run Todo in your case.
The book suggested Todo.start! to run the application from the config.ru file, but the Sinatra documentation example uses run Sinatra::Application. So I just changed the line from Todo.start! to
run Todo
That seems to work, but I'll have to look into the consequences.

Sidekiq web panel shows forbidden

I have mounted the Sidekiq panel in my Sinatra app like this:
require 'rubygems'
require 'bundler'
require 'sidekiq/web'
env = ENV['RACK_ENV'].to_sym || :development
Bundler.require(:default, :sinatra, env)
disable :run
Encoding.default_external = Encoding::UTF_8
set :environment, env
use Rack::ShowExceptions
use Rack::Session::Pool
use Rack::MethodOverride
Sidekiq::Web.use Rack::Session::Pool
require File.expand_path '../app/my_app.rb', __FILE__
run Rack::URLMap.new("/" => MyApp.new, "/sidekiq" => Sidekiq::Web.new)
This means my app is accessible through / and the sidekiq web panel through /sidekiq.
Now when I try to delete a job, I always get Forbidden. I read here https://github.com/mperham/sidekiq/issues/1289 and here https://github.com/mperham/sidekiq/issues/2487 but wether upgrading to rack-protection > 1.5.1 nor setting a session for Sidekiq::Web has solved the problem so far.
I'm starting my server with rackup using WEBrick, so I think this shouldn't be a server problem.
I'm using sinatra 1.4.2 with sidekiq 3.5.1. Any ideas on how to solve this?
I've found a solution. First I updated from sidekiq 3.4.2 to 4.0.1 and from sinatra 1.4.2 to 1.4.6. No problems so far.
The problem with the Forbidden message was a missing authenticity token for sidekiq's web panel. By adding the following lines, it worked:
require 'rubygems'
require 'bundler'
require 'sidekiq/web'
#####################################
# added a require for rack/protection
require 'rack/protection'
#####################################
env = ENV['RACK_ENV'].to_sym || :development
Bundler.require(:default, :sinatra, env)
disable :run
Encoding.default_external = Encoding::UTF_8
set :environment, env
use Rack::ShowExceptions
use Rack::Session::Pool
use Rack::MethodOverride
#####################################
# tell sinatra to use rack's protection methods
use Rack::Protection
#####################################
require File.expand_path '../app/my_app.rb', __FILE__
run Rack::URLMap.new("/" => MyApp.new, "/sidekiq" => Sidekiq::Web.new)
Also have a look at https://github.com/sinatra/rack-protection where all the protection methods are listed.
You may need to add this to application.rb (or an initializer like config/initializers/sidekiq.rb):
Sidekiq::Web.instance_variable_get(:#middleware).delete_if do |middleware|
middleware.first == Rack::Protection
end
That comes from a recent commit, but it's only applied to production and staging environments.
Detailed explanation about this problem.

Why is my Sinatra app showing a blank screen?

My Sinatra app was working on my local server a few days ago, but now it simply isn't running, and all I get is a blank screen. Even when I deploy to Heroku, nothing works.
Here's a GitHub repository where you can check out my code: https://github.com/aayalur/Sinfoursq
I think the problem is with my config.ru file.
# Gemfile
#require "rubygems"
#require "bundler/setup"
#require "sinatra"
require "./main"
set :run, false
set :raise_errors, true
run Sinatra::Application
Thanks!
You app doesn't display anything because you haven't closed a script tag. You would have noticed that if you inspected the DOM.
By the way, you start your app twice: once in main.rb, and then in config.ru.
You can notice that when Ctrl+C'ing the app: it starts again and you gotta shut it down a second time.
Since your app needs to be run on Rack, get rid of the Sinatra.run! if __FILE__ == $0 and start it with rackup, as explained in Sinatra's documentation.
This will fix the problems related to running the app.
In your config.ru leave this way:
require "rubygems"
require "bundler"
Bundler.require
require "./app"
run Sinatra::Application

Sinatra configuring environments on the fly

I have successfull written a little Sinatra application and already successfully deployed it on heroku.
However I want to run that application in development mode on my local computer and I want to have it production mode on heroku once I push it to the remote repository.
Currently I can achieve either one of thos options. When I change my config.ru to the following values:
require 'rubygems'
require 'sinatra'
require 'sinatra/reloader'
require "./calc.rb"
enable :logging
set :environment, :development
set :port, 4567
I'm able to run it locally (on port 4567) via ruby config.ru. When I change the config.ru to this:
require 'rubygems'
require 'sinatra'
require 'sinatra/reloader'
require "./calc.rb"
enable :logging
set :environment, :production
set :port, 4567
run Sinatra::Application
I'm able to get it to run on Heroku (on port 80).
But I can not use the same configuration for both development and production.
I would like to have something like:
ruby config.ru dev for development and ruby config.ru for production.
Additional information:
When I try to run the production config.ru on my local machine I get:
$ ruby config.ru
(eval):2:in `method_missing': undefined method `run' for main:Object (NoMethodError)
from (eval):4:in `__send__'
from (eval):4:in `method_missing'
from config.ru:10
C:\>type tmp.ru
require 'sinatra'
configure(:production){ p "I'm production" }
configure(:development){ p "I'mma dev mode" }
configure(:sassycustom){ p "I'mma own mode" }
exit!
C:\>rackup tmp.ru
"I'mma dev mode"
C:\>rackup -E development tmp.ru
"I'mma dev mode"
C:\>rackup -E production tmp.ru
"I'm production"
C:\>rackup -E sassycustom tmp.ru
"I'mma own mode"
C:\>rackup -E notdefined tmp.ru
If you don't specify an environment, development is used by default. You can specify any environment name you want, though 'production' is very common. If you specify an environment that you don't configure, no configuration block will match. (It might be a mistake on your part, but it's not an error caught by the code.)
Note that the Sinatra documentation says that setting RACK_ENV environment variable will be used if available. This used to not work, but some time in the last few years it has been fixed!
If, for example, you can set an environment variable for your service, you can control the mode.
You can also grab ENV['RACK_ENV'] in your config.ru and use that configure your app differently. On Heroku it should run in production by default and if you rackup to fire up your server it will be development by default. Here's some sample code from one of my apps that runs in both environments with the same config file:
#\ -p 4567
require 'bundler' # gem requires
Bundler.require(:default, ENV['RACK_ENV'].to_sym) # only loads environment specific gems
if ENV['RACK_ENV'] == 'production' # production config / requires
require './lib/middleware/exceptionmailer'
use Rack::ExceptionMailer,
:to => ['me#example.com'],
:from => 'service#example.com',
:subject => 'Error Occurred on Rack Application'
else # development or testing only
use Rack::ShowExceptions
end
This way, Thin or Passenger or whatever will pick it up and the right modules will get loaded in production but you can do other configuration for development.
Look at the Heroku documentation:
http://devcenter.heroku.com/articles/rack#frameworks
That's basically what I use for my app, when I start it locally it runs on port 4567.

Resources