Capistrano & Puma, configuration - ruby-on-rails

When deploying with Capistrano and use it to start the puma server you just include require 'puma/capistrano' and it does the magic when using cap deploy etc.
But how do i configure the puma server? I mean, let's say I want to change how many workers, cluster mode etc.
Solution: Found this in puma/capistrano.rb
def config_file
#_config_file ||= begin
file = fetch(:puma_config_file, nil)
file = "./config/puma/#{puma_env}.rb" if !file && File.exists?("./config/puma/#{puma_env}.rb")
file
end
end
So I guess I can just put a config file in that directory and it will work.
/config/puma/production.rb

Solution: Found this in puma/capistrano.rb
def config_file
#_config_file ||= begin
file = fetch(:puma_config_file, nil)
file = "./config/puma/#{puma_env}.rb" if !file && File.exists?("./config/puma/#{puma_env}.rb")
file
end
end
Just add the options in this file, if it's production, otherwise change the file name to appropriate environment.
/config/puma/production.rb

Related

Rails: system process won't start in rails server, but will in rails console

I want to start a ngrok process when server starts. To achieve this, I coded a ngrok.rb lib and I call it within an initializer
app/lib/ngrok.rb
require "singleton"
class Ngrok
include Singleton
attr_accessor :api_url, :front_url
def start
if is_running?
return fetch_urls
end
authenticate
started = system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &")
system("sleep 1")
if !started
return { api: nil, front: nil }
end
urls = fetch_urls
sync_urls(urls["api_url"], urls["front_url"])
return urls
end
def sync_urls(api_url, front_url)
NgrokSyncJob.perform_later(api_url, front_url)
end
def is_running?
return system("ps aux | grep ngrok")
end
def restart
stop
return start
end
def stop
return system("pkill ngrok")
end
def authenticate
has_file = system("ls ~/.ngrok2/ngrok.yml")
if has_file
return true
else
file_created = system("ngrok authtoken #{ENV['NGROK_TOKEN']}")
if file_created
return system("cat " + ENV['APP_ROOT'] + '/essentials/ngrok/example.yml >> ~/.ngrok2/ngrok.yml')
else
return false
end
end
end
def fetch_urls
logfile = ENV['APP_ROOT'] + '/log/ngrok.log'
file = File.open logfile
text = file.read
api_url = nil
front_url = nil
text.split("\n").each do |line|
next if !line.include?("url=") || !line.include?("https")
if line.split("name=")[1].split(" addr=")[0] == "ncommerce-api"
api_url = line.split("url=")[1]
elsif line.split("name=")[1].split(" addr=")[0] == "ncommerce"
front_url = line.split("url=")[1]
end
end
file.close
self.api_url = api_url
self.front_url = front_url
res = {}
res["api_url"] = api_url
res["front_url"] = front_url
return res
end
end
config/initializers/app-init.rb
module AppModule
class Application < Rails::Application
config.after_initialize do
puts "XXXXXXXXXXXXXXXXXXXXXXX"
Ngrok.instance.start
puts "XXXXXXXXXXXXXXXXXXXXXXX"
end
end
end
When I type rails serve, here is a sample of the output
So we know for sure my initializer is being called, but when I look at rails console if it's running, it's not!
But when I type Ngrok.instance.start in rails console, here's the output:
And it starts!
So, my question is: WHY ON EARTH is system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &") NOT working on rails serve, but it is on rails console?
UPDATE
If I use 'byebug' within ngrok.rb and use rails serve, when I exit byebug with "continue", the ngrok process is created and works
You're creating an orphaned process in the way that you use system() to start the ngrok process in the background:
system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &")
Note the & at the end of the commandline.
I'd need more details about your runtime environment to tell precisely which system policy kills the orphaned ngrok process right after starting it (which OS? if Linux, is it based on systemd? how do you start rails server, from a terminal or as a system service?).
But what's happening is this:
system() starts an instance of /bin/sh to interpret the commandline
/bin/sh starts the ngrok process in the background and terminates
ngrok is now "orphaned", meaning that its parent process /bin/sh is terminated, so that the ngrok process can't be wait(2)ed for
depending on the environment, the terminating /bin/sh may kill ngrok with a SIGHUP signal
or the OS re-parents ngrok, normally to the init-process (but this depends)
When you use the rails console or byebug, in both cases you're entering an interactive environment, which prepares "process groups", "session ids" and "controlling terminals" in a way suitable for interactive execution. These properties are inherited by child processes, like ngrok. This influences system policies regarding the handling of the orphaned background process.
When ngrok is started from rails server, these properties will be different (depending on the way rails server is started).
Here's a nice article about some of the OS mechanisms that might be involved: https://www.jstorimer.com/blogs/workingwithcode/7766093-daemon-processes-in-ruby
You would probably have better success by using Ruby's Process.spawn to start the background process, in combination with Process.detach in your case. This would avoid orphaning the ngrok process.

How can I use Rails 5.2 credentials in capistrano's deploy.rb file?

I've just updated my Rails app to 5.2, and configured it to use the new config/credentials.yml.enc file.
When I try to deploy, I get this error:
NameError: uninitialized constant Rails
/Users/me/Documents/project/config/deploy.rb:27:in `<top (required)>'
That's pointing to this line in my config/deploy.rb file:
set :rollbar_token, Rails.application.credentials[:rollbar_token]
So it appears that while capistrano is running, it doesn't have access to Rails.application.credentials.
How are you all handling this? I've got some ideas...
Set this one variable as an ENV variable
I don't love how this separates/customizes this one setting
Somehow make it so capistrano has access to Rails.application.credentials
I don't know if this is a good idea or if there are other things I need to be aware of if I go this route
Remove deploy tracking in rollbar
🤷‍♂️
Put the following line(s) on top of your config/deploy.rb
# config/deploy.rb
require File.expand_path("./environment", __dir__)
This include make constants like Rails.application accessible in files like config/deploy/production.rb. Now things like the following are possible:
# config/deploy/staging.rb
server "production.lan", user: "production", roles: %w{app db web}
set :stage, :production
set :branch, "development"
set :pg_password, Rails.application.credentials[:staging][:postgres][:password]
I solved the problem as follows:
set :rollbar_token, YAML.load(`rails credentials:show`)['rollbar_token']
1. Upload master.key the file on the server (user read-only) like so:
namespace :setup do
desc "setup: copy config/master.key to shared/config"
task :copy_linked_master_key do
on roles(fetch(:setup_roles)) do
sudo :mkdir, "-pv", shared_path
upload! "config/master.key", "#{shared_path}/config/master.key"
sudo :chmod, "600", "#{shared_path}/config/master.key"
end
end
before "deploy:symlink:linked_files", "setup:copy_linked_master_key"
end
Put it in your lib/capistrano/tasks/setup.rake
2. Ensure file is linked
In deploy.rb:
set :linked_files, fetch(:linked_files, []).push("config/master.key")
3. Ensure Capfile loads the task:
Make sure your Capfile has the line
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
require File.expand_path("./environment", __dir__)
puts App::Application.credentials.rollbar_token
The way I solve this is to declare a $ROLLBAR_ACCESS_TOKEN environment variable on the server. I place it at the top of ~deployer/.bashrc like this:
export ROLLBAR_ACCESS_TOKEN=...
Then I integrate with Capistrano by defining this task:
task :set_rollbar_token do
on release_roles(:all).first do
set :rollbar_token, capture("echo $ROLLBAR_ACCESS_TOKEN").chomp
end
end
before "rollbar:deploy", "set_rollbar_token"
It seemed to me non-ideal to load the whole of Rails here, or to have to read command line output, so here's an alternative solution:
require "active_support/encrypted_configuration"
require "active_support/core_ext/hash/keys"
module CredentialLoader
def read_credentials(environment:)
YAML.load(
ActiveSupport::EncryptedConfiguration.new(
config_path: "config/credentials/#{environment}.yml.enc",
key_path: "config/credentials/#{environment}.key",
env_key: environment.to_s,
raise_if_missing_key: true
).read
)
end
end
Then you can do this in your deploy.rb:
include CredentialLoader
set :rollbar_token, read_credentials(environment: fetch(:stage))["rollbar_access_token"]

How to disable "Cannot Render Console from..." on Rails

I'm using Ubuntu/vagrant as my development environment.
I'm getting these messages on rails console:
Started GET "/assets/home-fcec5b5a277ac7c20cc9f45a209a3bcd.js?body=1" for 10.0.2.2 at 2015-04-02 15:48:31 +0000
Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Is it possible to disable those "cannot render..." messages or allow them in any way?
You need to specifically allow the 10.0.2.2 network space in the Web Console config.
So you'll want something like this:
class Application < Rails::Application
config.web_console.permissions = '10.0.2.2'
end
Read here for more information.
As pointed out by pguardiario, this wants to go into config/environments/development.rb rather than config/application.rb so it is only applied in your development environment.
You can whitelist single IP's or whole networks.
Say you want to share your console with 192.168.0.100. You can do this:
class Application < Rails::Application
config.web_console.whitelisted_ips = '192.168.0.100'
end
If you want to whitelist the whole private network, you can do:
class Application < Rails::Application
config.web_console.whitelisted_ips = '192.168.0.0/16'
end
If you don't wanna see this message anymore, set this option to false:
class Application < Rails::Application
config.web_console.whiny_requests = false
end
Be careful what you wish for, 'cause you might just get it all
This is probably only for development purposes so you might prefer to place it under config/environments/development.rb instead of config/application.rb.
Hardcoding an IP into a configuration file isn't good. What about other devs? What if the ip changes?
Docker-related config should not leak into the rails app whenever possible. That's why you should use env vars in the config/environments/development.rb file:
class Application < Rails::Application
# Check if we use Docker to allow docker ip through web-console
if ENV['DOCKERIZED'] == 'true'
config.web_console.whitelisted_ips = ENV['DOCKER_HOST_IP']
end
end
You should set correct env vars in a .env file, not tracked into version control.
In docker-compose.yml you can inject env vars from this file with env_file:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
links:
- db
environment:
- DOCKERIZED=true
env_file:
- ".env"
Based on the feebdack received in comments, we can also build a solution without environment variables:
class Application < Rails::Application
# Check if we use Docker to allow docker ip through web-console
if File.file?('/.dockerenv') == true
host_ip = `/sbin/ip route|awk '/default/ { print $3 }'`.strip
config.web_console.whitelisted_ips << host_ip
end
end
I'll leave the solutions with env var for learning purposes.
Auto discovery within your config/development.rb
config.web_console.whitelisted_ips = Socket.ip_address_list.reduce([]) do |res, addrinfo|
addrinfo.ipv4? ? res << IPAddr.new(addrinfo.ip_address).mask(24) : res
end
Of course might need to add
require 'socket'
require 'ipaddr'
Within your file.
Anyone on any of my private networks is welcome.
I run in a docker container and I don't care which network it wants to use this week.
config/environments/development.rb add line
config.web_console.whitelisted_ips = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
For development environment: Detect if it's docker, then determine the IP address and whitelist it
# config/environments/development.rb
require 'socket'
require 'ipaddr'
Rails.application.configure do
...
# When inside a docker container
if File.file?('/.dockerenv')
# Whitelist docker ip for web console
# Cannot render console from 172.27.0.1! Allowed networks: 127.0.0.1
Socket.ip_address_list.each do |addrinfo|
next unless addrinfo.ipv4?
next if addrinfo.ip_address == "127.0.0.1" # Already whitelisted
ip = IPAddr.new(addrinfo.ip_address).mask(24)
Logger.new(STDOUT).info "Adding #{ip.inspect} to config.web_console.whitelisted_ips"
config.web_console.whitelisted_ips << ip
end
end
end
For me this prints the following and the warning goes away 🎉
Adding 172.27.0.0 to config.web_console.whitelisted_ips
Adding 172.18.0.0 to config.web_console.whitelisted_ips
My solution was to combine
the answer from user2481743 ⭐️ https://stackoverflow.com/a/42142563/2037928
the comment from jottr ⭐️ How to disable "Cannot Render Console from..." on Rails
If you run your site locally (on the host) it generally works out, since 127.0.0.1 is always permitted. But if you're going to put your site into a container (not in production, locally), you might want to add this into config/environments/development.rb:
require 'socket'
require 'ipaddr'
Rails.application.configure do
...
config.web_console.permissions = Socket.getifaddrs
.select { |ifa| ifa.addr.ipv4_private? }
.map { |ifa| IPAddr.new(ifa.addr.ip_address + '/' + ifa.netmask.ip_address) }
...
end
P.S. Most of the time you want it to whine (don't want to do config.web_console.whiny_requests = false). Because it might mean you're running web-console in production (which you shouldn't do).
For me, whitelisted_ips didn't seem to work in a new project. The Readme states the corresponding configuration entry is supposed to be permissions now:
Rails.application.configure do
config.web_console.permissions = '192.168.0.0/16'
end
https://github.com/rails/web-console/blob/master/README.markdown
If you want to stop seeing this error message you can add this line in development.rb
config.web_console.whiny_requests = false
Note that only the last 'config.web_console.whitelisted_ips' will be used. So
config.web_console.whitelisted_ips = '10.0.2.2'
config.web_console.whitelisted_ips = '192.168.0.0/16'
will only whitelist 192.168.0.0/16, not 10.0.2.2.
Instead, use:
config.web_console.whitelisted_ips = ['10.0.2.2', '192.168.0.0/16']
class Application < Rails::Application
config.web_console.whitelisted_ips = %w( 0.0.0.0/0 ::/0 )
end
If you are using Docker most likely you don't want neither to introduce new ENV variables nor to hardcode your specific IP address.
Instead you may want to check that you are in Docker using /proc/1/cgroup, and to allow your host IP (both for web_console and better_errors). Add to your config/environments/development.rb
# https://stackoverflow.com/a/20012536/4862360
if File.read('/proc/1/cgroup').include?('docker')
# https://stackoverflow.com/a/24716645/4862360
host_ip = `/sbin/ip route|awk '/default/ { print $3 }'`.strip
BetterErrors::Middleware.allow_ip!(host_ip) if defined?(BetterErrors::Middleware)
config.web_console.whitelisted_ips << host_ip
end
I just want to add this because my own mistake caused me to get the same error.
I was missing this from the controller
load_and_authorize_resource except: [:payment_webhook]
Basically I was using cancancan to place authorization on that route, which was causing a 404 to be returned. I saw the message and assumed the two were related, when in fact they were not
Cannot render console from xxxxxx Allowed networks: xxxxx
So if you are getting an error message, it's possible that it has nothing to do with the Cannot render console from xxxxxx Allowed networks: xxxxx you see - look for other problems!

How do I have different envirenment variable values in Rails?

This is how I run rails console command:
COMPANY=b2b RAILS_ENV=development DEPLOY_ENV=localhost rails console
Instead I want to run only rails console command by setting these variables internally. How do I do that?
I saw some answers and made the config/company.yml file:
development:
COMPANY: b2b
DEPLOY_ENV: localhost
production:
COMPANY: b2c
DEPLOY_ENV: xyz.com
And I wrote the following config/initializers/company.rb file:
COMPANY_CONFIG = YAML.load_file("#{::Rails.root}/config/company.yml")[::Rails.env]
How do I access these variables in some helper/controller? I didn't understand how to do that? How do I set these ENV variables after running only rails console or rails server without passing the extra environment variable shown above?
You can't set environment variables in yaml file this difference things. Use dotenv gem for manage environment variables.
Read first Environment Variables
But have dirty hack. You can create ENV variable when rails loaded
add this code to before_configuration in config/application.rb:
config.before_configuration do
env_file = File.join(Rails.root, 'config', 'company.yml') # Find yml file
YAML.load(File.open(env_file)).each do |key, value| # Open file
ENV[key.to_s] = value # Set value to ENV
end if File.exists?(env_file) # If file exists
end
And now you can access to env variables from yml file anywhere in app.
I do not test this but i think this work.
This work.
Some info
You can create these settings variables in the config/initializers/application.rb file:
company_settings_path = Rails.root.join('config', 'company.yml')
COMPANY_CONFIG = { development: { company: :default, deploy_env: :none_yet } }
COMPANY_CONFIG = YAML::load(File.open(company_settings_path)) if File.exists?(company_settings_path)
COMPANY_CONFIG = COMPANY_CONFIG.with_indifferent_access
And then access them like this:
# controller
def show
if COMPANY_CONFIG[:company] == 'b2c'
# your logic
else
# other logic
end
end
You can acces the constant COMPANY_CONFIG everywhere in your app (models, controllers, views, helpers) the same way I used below.

Rails/Passenger sub URI error

I am attempting to deploy a Rails 3.0.0 application to a sub URI using passenger 2.2.15.
I believe I've made the correct RailsBaseURI changes to my http.conf , have symlinked the sub URI to the public directory of my app and added the following line of code to environments/production.rb:
config.action_controller.relative_url_root = "/sub_uri"
I've done this several times pre-rails3.0.0. That said, the app won't launch. It fails with the following Passenger error:
Error Message: wrong number of arguments(1 for 0)
Exception class: ArgumentError
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.0/lib/action_controller/railtie.rb 54 in `relative_url_root='
Is there an incompatibility between passenger 2.2.15 and rails 3.0.0 that affects sub URI's?
Any help sorting out this error is greatly appreciated.
The setter is depreciated, it's nowhere to be found in actionpack/lib/action_controller/railtie.rb.
As seen here (actionpack/lib/action_controller/depreciated/base.rb):
module ActionController
class Base
# Deprecated methods. Wrap them in a module so they can be overwritten by plugins
# (like the verify method.)
module DeprecatedBehavior #:nodoc:
def relative_url_root
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root is ineffective. " <<
"Please stop using it.", caller
end
def relative_url_root=
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
"Please stop using it.", caller
end
end
end
end
In actionpack/lib/action_controller/metal/compatibility.rb you can see it's setter is an ENV variable:
self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
So you need to set the ENV variable: RAILS_RELATIVE_URL_ROOT="/sub_uri"
To set the environment variable add:
SetEnv RAILS_RELATIVE_URL_ROOT /sub_uri
To the VirtualHost section (or similar) of your apache config then make sure it's being read by restarting apache and passenger.
cd <your_rails_project>
sudo apache2ctl graceful
touch tmp/restart

Resources