Set database.yml using environment variables in Sinatra - mysql

I have a very weird requirement in a project I am working on.
I have file stored in some location /etc/config/config.json which contains database information like Host, Port, Username or Passwordor roughly looks as below:
{
"mysql-db": {
"host": "172.17.0.27",
"port": 3306,
"password": "root",
"username": "root"
}
}
I am building my small web-app based on Sinatra and using sinatra-activerecord for working with MySql database.
My database.yml file looks like this:
development:
adapter: mysql2
database: toopaste
host: <%= ENV["MYSQL_DB_HOST"] %>
port: <%= ENV["MYSQL_DB_PORT"] %>
username: <%= ENV["MYSQL_DB_USERNAME"] %>
password: <%= ENV["MYSQL_DB_PASSWORD"] %>
What I was trying?
I created a executable file like setup.rb as:
#! /usr/bin/env ruby
require 'json'
FILE_PATH = "/etc/atlantis/config/konfig.json"
data = JSON.parse(File.read(FILE_PATH))
system("export MYSQL_DB_HOST=#{data['mysql-db']['host']}")
system("export MYSQL_DB_PORT=#{data['mysql-db']['port']}")
system("export MYSQL_DB_USERNAME=#{data['mysql-db']['username']}")
system("export MYSQL_DB_PASSWORD=#{data['mysql-db']['password']}")
This doesn't set env variables MYSQL_DB_HOST or others variables to be used by config/database.yml file.
Any idea how to accomplish such job?
One way I could think is "Dynamically generate whole database.yml file after reading config.json params.
But would like to know if there is better solution available.

I have a feeling that Sinatra doesn't process the environment variables into the database.yml file. Rails does... You can do this, but I think it's a bit of a faff. I think you have to put the YML file through as an ERB template or something.
Some other options:
Dynamically write the entire database.yml file from your setup.rb - although I wouldn't do this. The load it form there as normal.
Or, use the Sinatra config to set your Database connections details form your preferred file. Example in the sinatra-activerecord read me.
set :database, {adapter: 'mysql', database: ENV['MY_SQL_DB_HOST']}
This would seem cleaner to me. In fact, I would go a little further and use Sinatra config to do the whole thing (load the file, and get the parameters from there). This way the code becomes more explicit, and easier to change in the future. i.e., and this is just off the top of my head, so you may need to adjust:
configure do
FILE_PATH = "/etc/atlantis/config/konfig.json"
data = JSON.parse(File.read(FILE_PATH))
set :DB_PASSWORD = data['mysql-db']['password']
# ETC...
set :database, { ... }
end
Hope this helps.

I assume this configuration file is outside of your application's reach otherwise you could just read the json file directly. Otherwise you could have a parser of the JSON and transform that into YAML. Something like this would work:
require 'json'
require 'yaml'
json = JSON.parse(File.read('./test.json'))
database = Hash.new
database[:development] = json
File.open("./test.yaml","w"){|h| h.write database.to_yaml }
Your YAML should look like this.
$ cat test.yaml
---
:development:
mysql-db:
host: 172.17.0.27
port: 3306
password: root
username: root
I assume you can add the additional parameters to the hash to get something like the bellow configurations.
config/production.yaml
database:
adapter: mysql2
host: localhost
port: 3306
database: myappdb
username: myprodusername
password: myprodpassword
config/development.yaml
database:
adapter: mysql2
host: localhost
port: 3306
database: myappdb_dev
username: mydevuser
password: mydevpassword
And then load them into your app like this.
config.ru
require 'sinatra'
require 'yaml'
configure :production do
#config = YAML.load_file("config/#{ENV["RACK_ENV"]}.yaml")
#some other things that you do in prod
end
configure :development do
#config = YAML.load_file("config/#{ENV["RACK_ENV"]}.yaml")
#some other things that you only do in dev
end
Starting the app
$ RACK_ENV=development puma (or whatever other server you use like thin)
or for prod
$ RACK_ENV=production puma (or whatever other server you use like thin)

The system method creates a new subshell and executes the command in it. When the command sets an environment variable then that environment variable is set in that subshell, the change does not propagate back to the parent process. This means that those environment variables are not being set.
To set environment variables in the current process, you can simply access ENV directly. So instead of system("export MYSQL_DB_HOST=#{data['mysql-db']['host']}") do this:
ENV['MYSQL_DB_HOST'] = data['mysql-db']['host']
(and similarly for the other settings).
This should fix your problem, but since you are reading the settings yourself directly from the json config file there arguably isn’t much point using environment variables. It would be more direct to access the settings more directly from the Erb/Yaml file. Ideally you’d want to pass the settings to the Erb evaluation in some way, but Sinatra-ActiveRecord doesn’t appear to allow you to set the binding or context of the Erb evaluation in any way. An alternative could be to use a global variable for the data hash:
# When reading the json, use a global instead,
# and pick out the 'mysql-db' key
$data = JSON.parse(File.read(FILE_PATH))['mysql-db']
The in the database.yml:
development:
adapter: mysql2
database: toopaste
host: <%= $data['host'] %>
port: <%= $data['port'] %>
username: <%= $data['username'] %>
password: <%= $data['password'] %>

Related

mysql: Access denied for user

In my rails app I have this configuration for database
adapter: mysql2
host: *****
username: *****
password: <%= ENV['MYSQL_PW'] %>
database: *****
encoding: utf8
timeout: 5000
pool: 5
It is working perfectly in the server. But recently there was a bug and I tried to access rails console, but I get this error
Access denied for user '****' (using password: NO) (Mysql2::Error).
I also I tried to run migration and I get same error again. I don't understand what is the problem here. How can I solve this?
Also how can I check if ENV['MYSQL_PW'] is set in the unix environment variable?
Here is my log
$ rake db:migrate
DEPRECATION WARNING: The configuration option `config.serve_static_assets` has been renamed to `config.serve_static_files` to clarify its role (it merely enables serving everything in the `public` folder and is unrelated to the asset pipeline). The `serve_static_assets` alias will be removed in Rails 5.0. Please migrate your configuration files accordingly. (called from block in <top (required)> at )
DEPRECATION WARNING: You did not specify a `log_level` in `production.rb`. Currently, the default value for `log_level` is `:info` for the production environment and `:debug` in all other environments. In Rails 5 the default value will be unified to `:debug` across all environments. To preserve the current setting, add the following line to your `production.rb`:
config.log_level = :info
. (called from block in tsort_each )
rake aborted!
Mysql2::Error: Access denied for user '****' (using password: NO)
Don't forget to specify RAILS_ENV when you are running your commands.
By default rails assumes the environment is development, but as I see here you want to run those on production.
Simply do
RAILS_ENV=production rake db:migrate
RAILS_ENV=production rails c
# or alternatively
rails c -e production
http://guides.rubyonrails.org/command_line.html
Try the following:
Edit database.yml password to be a string and not an ENV variable, to identify the problem.
To test the ENV Variable, log in the rails console with rails c and input ENV['YourVariable'] to see if it is set
You can solve this problem by sourcing your bash_profile file if you have Unix system (linux/ubuntu/mac) in the terminal input source ~/.bash_profile
If you have Rails 4 upon you should run the following terminal command spring stop
In database.yml you should include the ENV Variable with the following syntax, beacuse yml needs the .erb syntax. <%= ENV['YOUR VARIABLE'] %>
Failing to access environment variables within `database.yml` file
The ENV Variable is Case Sensitive
I had this problems and I was able to solve it, but I keep having them. For this reason I read several discussions:
Rails 4.1 environment variables not reloading
Sorry if I was not able to help more
Fabrizio Bertoglio

How can I extract data from one database, manipulate it, and insert it into another in Rails?

I have two databases (a legacy MySQL one and a new PostgreSQL one), and the schema for the new one was redesigned. As a result, I can't just dump the old database to YAML and load it into the the new one, since columns are named different things and may need to be manipulated. Is there an elegant way to do this?
It's actually fairly easy.
First you need to define the connection to your MySQL database in your database.yml. Let's call it legacy:
development:
adapter: postgresql
.....
test:
adapter: postgresql
.....
legacy:
adapter: mysql2
encoding: utf8
database: your_old_mysql_db
username: root
password:
host: localhost
port: 3306
You will need the mysql2 gem in your gemfile, alongside the pg gem!
Now just create models for each of the tables you want to connect to:
Here's one called LegacyUser, which will let you get the old users out of your MySQL database:
# app/models/legacy_user.rb
class LegacyUser < ActiveRecord::Base
establish_connection :legacy
self.table_name = "whatever_your_my_sql_user_table_name_is"
end
Now, in a Rake task you can pull data out of the MySQL table and stick it into your Postgres table like so:
# lib/tasks/import.rake
namespace :import do
desc "Import Users"
task users: :environment do
puts ""
puts "Importing Legacy Users:"
LegacyUser.find_each do |lu|
print "#{lu.id} - #{lu.first_name}"
u = User.new
u.email = lu.email
u.first_name = lu.first_name
u.last_name = lu.last_name
if u.save
puts "... saved"
else
puts "... bad: #{u.errors.full_messages.join(',')}"
end
end
end
end
Now you can just run:
rake import:users

How to create an if statement in database.yml to use different databases based on the current user in rails

as i explained i want to use different databases based on the user.
I got a partner table created with devise and i have multiple databases that i created.Everyone of those databases have the same schema and same relationships.
What i want to accomplish is something like
default: &default
adapter: mysql2
pool: 5
timeout: 5000
development:
<<: *default
database: db_ <%= current_partners%>
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: db_ <%= current_partners%>
production:
<<: *default
database: db_ <%= current_partners%>
why can't i use this statement ??
Btw i m using mysql2 as seen in the code fragment.
In your database.yml, just mention default database configuration. Whenever, you want to switch between databases, call user defined switch_database method to connect to another database before making hit to corresponding database. Something like:
def switch_database
establish_connection (
:adapter => "mysql2",
:host => "another_host_name",
:username => "username",
:password => "password",
:database => "db_#{current_partner}"
)
end
Place above method in appropriate file as per your convenient to call it.
database.yml is loaded on application boot, variables like current_partners are not available at this point as they are request specific. You need to create all the databases within your database.yml and then tell rails which one to use in your controller. This however may cause a lot of concurrency issues (not sure though, never actually did this - see below).
Note however that this is not considered the best practice. Why not to have partners models and then have an associations instead?
You might want to checkout these gems which can help you achieve multi-tenancy in your Rails application here.
Apartment is one of quite active and Rack based database multi-tenancy ruby gems. I hope it helps.

How to select the connection to database when I use console in symfony2

I have two databases, (MySQL and Oracle), I did the connection betweek sf2 and both databases, here is my config.yml file:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver, add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# path: "%database_path%"
sysman:
driver: %database_driver2%
host: %database_host2%
port: %database_port2%
dbname: %database_name2%
user: %database_user2%
password: %database_password2%
charset: UTF8
My question is, how can I run console command on the second database (Oracle), commands like (doctrine:database:create ...), and thanks
Use the --connection parameter:
php app/console doctrine:database:create --connection=default
or
php app/console doctrine:database:create --connection=sysman
You should first read a tutorial about commands and how to pass options and parameters to the commands. And how to distinguish between an option and a parameter.
If you want to make your own commands...
You will probably want to make it like this - if you do not pass an option (you will use the default database), if you pass it, you will make sure it is a valid option, and use the passed database connection name.
Doctrine is not tightly coupled with Mysql, you can use almost all most common available databases.
Also note, commands are container aware. That means you commands can access container, though which you have access to your services, such as doctrine:
protected function execute(InputInterface $input, OutputInterface $output)
{
$connection $input->getArgument('connection');
# Validate connection argument here and use it below
$container = $this->getContainer();
$em = $container->get('doctrine')->getManager(); // default db
$em = $container->get('doctrine')->getManager('sysman'); // another
return 1;
}
I wrote the code without testing, excuse me for any mistake I might have done.
php app/console doctrine:mapping:info --em=default (same without em option)
php app/console doctrine:mapping:info --em=sysman

Generate a model that uses pre-existing data from database

Im making an application with a pre-existing database, I set up the database.yml to use the database.
database.yml
development:
adapter: mysql2
encoding: utf8
# database: ttlem_demo_development
database: ttle
pool: 5
username: root
password:
socket: /tmp/mysql.sock
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: mysql2
encoding: utf8
database: ttlem_demo_test
pool: 5
username: root
password:
socket: /tmp/mysql.sock
production:
adapter: mysql2
encoding: utf8
database: ttlem_demo_production
pool: 5
username: root
password:
socket: /tmp/mysql.sock
I only want one table out of the database it is called account views, I try to generate a scaffold for this with all the correct fields but it tells me i need to migrate (when i render it in the browser), if i migrate i wont be able to use the existing data, is this correct? How can i make a model that will use the existing data and fields?
Thank you for all your help :)
Two things to try:...
#1 Run your scaffold without a migration so it doesn't think you're missing one.
rails g scaffold yourmodelname fieldone:fieldtype ... etc --no-migration
#2 If that doesn't work you can go the long way round but dumping and reloading with a valid schema version number
Add this db to yml to your gemfile:
gem 'yaml_db', github: 'jetthoughts/yaml_db', ref: 'fb4b6bd7e12de3cffa93e0a298a1e5253d7e92ba'
It works for either rails 3 or rails 4.
Do a schema dump of your current database so you'll get a schema.rb with a valid version number.
bundle exec rake db:schema:dump
Now that you have a valid schema dump your data.
bundle exec rake db:data:dump
Drop your database (you can do it manually using the mysql commands if you prefer or run rake db:drop)
Now recreate it with your schema file.
bundle exec rake db:schema:load
Now add back your data
bundle exec rake db:data:load
Start your server and assuming your correctly matched all your data fields in your model (so the proper strong parameters are set from your scaffold) you should be good.