Reputation: 8840
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 Password
or 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.
Upvotes: 1
Views: 4587
Reputation: 79743
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'] %>
Upvotes: 0
Reputation: 737
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)
Upvotes: 0
Reputation: 3434
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.
Upvotes: 1