Sidekiq web panel shows forbidden - ruby

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.

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!

Can't adapt the airbrake gem to a Sinatra app

I am using the airbrake gem like so:
require 'airbrake'
Airbrake.configure do |config|
config.api_key = 'XXXXX'
config.development_environments = ["development", "test", "cucumber"]
end
use Airbrake::Rack
enable :raise_errors
but it still sends airbrake notifications in development.
My environment is saved in ENV['RACK_ENV'].
I don't want to hack my way into this, is there an "outside" solution?
Also, I do want to raise exceptions in development - I just don't want them to be sent to airbrake..
You could use a configure block to only setup Airbrake in production:
configure :production do
require 'airbrake'
Airbrake.configure do |config|
config.api_key = 'XXXXX'
end
use Airbrake::Rack
end
If you have more than one environment you want Airbrake enabled in, you can specify a list, e.g.:
configure :production, :staging do
...
#matt's answer should work well, but if you want to do this in the rackup file when setting up the middleware instead of inside the Sinatra app, you could do:
use Airbrake::Rack if ENV['RACK_ENV'] == "production"
I quite often do this with middleware.

Sinatra Synchrony with Redis connection pooling

Is this the correct way of handling Redis connection pooling with Sinatra Synchrony?
My gemfile looks like this:
gem 'sinatra-synchrony'
gem 'hiredis'
gem 'redis'
The sinatra server files use the classic style approach, and generally look like so:
require 'sinatra'
require 'sinatra/synchrony'
require 'redis/connection/hiredis'
require 'redis/connection/synchrony'
require 'redis'
redis = EventMachine::Synchrony::ConnectionPool.new(size: 5) do
Redis.new(path: '/tmp/redis.sock')
end
get / do
# lots of redis reads and writes
end
I then launch multiple instances of the same server application, each under a different port, and use nginx to load balance between them.
Is this the proper solution for connection pooling Redis with Sinatra servers?
Here is a working example, I removed sinatra/sinatra because I don't feel it is needed and I couldn't make it works:
Gemfile:
source :rubygems
gem 'thin'
gem 'rack-fiber_pool'
gem 'hiredis'
gem 'sinatra'
gem 'em-synchrony'
gem 'redis'
config.ru:
require 'rubygems'
require 'bundler/setup'
require 'sinatra/base'
require 'redis/connection/synchrony'
require 'redis'
require 'rack/fiber_pool'
class App < Sinatra::Base
set :template_path, '/tmp'
def initialize
super
#redis = EventMachine::Synchrony::ConnectionPool.new(size: 2) do
Redis.new
end
end
get '/' do
#redis.multi do |r|
r.set('v', "value2")
r.set('v2', '43')
end
#redis.get('v')
end
end
use Rack::FiberPool
use Rack::CommonLogger
run App
And run it with (in the same folder):
bundle
bundle exec thin start
In a real application you would remove the application code from the config.ru file and add a require but at least it gives you a start :)

Why do I need to run my sinatra app again when making changes and my environment is not :development?

I just implemented Compass configuration for my Sinatra app but when I change the environment to :test or :production and modify my files like screen.sass or index.haml my changes are not reflected when I reload the page so I need to run my app again?
Is it normal? Is is just me?
This is how my app.rb file looks like:
require 'sinatra'
require 'haml'
require 'sass'
require 'compass'
require './helpers.rb'
configure do
set :environment, :test
Compass.configuration do |config|
settings.environment == :production ?
config.output_style = :compressed :
config.output_style = :nested
settings.environment == :development ?
config.line_comments = true :
config.line_comments = false
end
set :sass, Compass.sass_engine_options
end
before do
#js = 'javascript:;'
end
get '/scripts/jquery.js' do
# Downloads the latest jQuery 1.x version when needed. Requires to reload the page after done.
`curl "https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" >> public/scripts/jquery.js`
end
get '/styles/:name.css' do
sass :"styles/#{params[:name]}"
end
get '/?' do
haml :index
end
get '/:page/?' do
haml params[:page].to_sym
end
Any idea?
Generally, if you make a change to a running Sinatra application, you have to restart the application, as the program has already been loaded to memory.
There are options for automatically detecting changes and restarting the application on the Sinatra FAQ.
Since Shotgun fix the issue partially (reloading the files for your at production, maybe try with Sinatra::Reloader which, IMHO, works better than Shotgun.
Maybe something like (not tested)
require "sinatra"
configure(:production) do |c|
require "sinatra/reloader"
c.also_reload "*.sass", "*.haml"
end
That being said, are you sure you do need this kind of behavior on a production/test environment for updating? Development env. should be (at least, for what I use it for) for this kind of hot testing.
I used to use sinatra::reloader
but I didn't like the huge dependencies incurred (as should we all be mindful how many gems get activated)
pistol ( at a tender age of ver 0.0.2) and I think does the required job nicely
I use shotgum gem for this.
gem install shotgun
then
shotgun app.rb
from within the app dir
this then reloads the app per request, rather than holding the whole thing in memory. you access the site on localhost:9393

Reloading Sinatra app on every request on Windows

I've set up Rack::Reload according to this thread
# config.ru
require 'rubygems'
require 'sinatra'
set :environment, :development
require 'app'
run Sinatra::Application
# app.rb
class Sinatra::Reloader < Rack::Reloader
def safe_load(file, mtime, stderr = $stderr)
if file == Sinatra::Application.app_file
::Sinatra::Application.reset!
stderr.puts "#{self.class}: reseting routes"
end
super
end
end
configure(:development) { use Sinatra::Reloader }
get '/' do
'foo'
end
Running with thin via thin start -R config.ru, but it only reloads newly added routes. When I change already existing route, it still runs the old code.
When I add new route, it correctly reloads it, so it is accessible, but it doesn't reload anything else.
For example, if I changed routes to
get '/' do
'bar'
end
get '/foo' do
'baz'
end
Than / would still serve foo, even though it has changed, but /foo would correctly reload and serve baz.
Is this normal behavior, or am I missing something? I'd expect whole source file to be reloaded. The only way around I can think of right now is restarting whole webserver when filesystem changes.
I'm running on Windows Vista x64, so I can't use shotgun because of fork().
You could try sinatra-reloader, which is known to work well on Windows (also, it's faster than shotgun).
This works:
# config.ru
require 'rubygems'
require 'app'
set :environment, :development
run Sinatra::Application
# app.rb
require 'sinatra'
class Sinatra::Reloader < Rack::Reloader
def safe_load(file, mtime, stderr = $stderr)
if file == File.expand_path(Sinatra::Application.app_file)
::Sinatra::Application.reset!
stderr.puts "#{self.class}: reseting routes"
end
super
end
end
configure(:development) { use Sinatra::Reloader }
get '/' do
'foo'
end
It matters from where you have the require statement. But I find the following solution more elegant and robust:
# config.ru
require 'rubygems'
require 'sinatra'
require 'rack/reloader'
require 'app'
set :environment, :development
use Rack::Reloader, 0 if development?
run Sinatra::Application
# app.rb
Sinatra::Application.reset!
get '/' do
'foo'
end
Does Shotgun not work on Windows?
From the README:
Shotgun
This is an automatic reloading version of the rackup command that's shipped with
Rack. It can be used as an alternative to the complex reloading logic provided
by web frameworks or in environments that don't support application reloading.
The shotgun command starts one of Rack's supported servers (e.g., mongrel, thin,
webrick) and listens for requests but does not load any part of the actual
application. Each time a request is received, it forks, loads the application in
the child process, processes the request, and exits the child process. The
result is clean, application-wide reloading of all source files and templates on
each request.
You can also try using Trinidad a JRuby Rack container based on Tomcat. In my experience it does change reloading by default without having to modify your source files. Bloody fast too. Obviously no good if you are using native libraries, but if you are deploying on Windows you are probably used to adopting a pure-ruby approach.
Its syntax is just as simple as the thin approach:
jruby -S trinidad -r config.ru
There is no Java specific yak shaving (i.e. creating web.xml or WARing up your Ruby app) and the gem is simple to install.

Resources