Skip to content

Support for Rails.application.credentials in database.yml #19

@uyv-tbrothers

Description

@uyv-tbrothers

My database.yml file includes something like this:

production:
<<: *default
database: prod
host: localhost
username: <%= Rails.application.credentials.dig :postgres, :username %>
password: <%= Rails.application.credentials.dig :postgres, :password %>

Currently, this gem fails at task cap production postgres:backup:create with:

00:00 postgres:backup:ensure_remote_dirs
      01 mkdir -p my_path/shared/postgres_backup
     01 my_user@my_path 0.059s
#<Thread:0x00007ff89c960928@/Users/me/.rvm/gems/ruby-2.5.1/gems/sshkit-1.18.0/lib/sshkit/runners/parallel.rb:10 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
	1: from /Users/me/.rvm/gems/ruby-2.5.1/gems/sshkit-1.18.0/lib/sshkit/runners/parallel.rb:11:in `block (2 levels) in execute'
/Users/me/.rvm/gems/ruby-2.5.1/gems/sshkit-1.18.0/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as my_user@my_path: bundle exit status: 1 (SSHKit::Runner::ExecuteError)
bundle stdout: Nothing written
bundle stderr: (erb):28:in `<main>': uninitialized constant Rails (NameError)
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `eval'
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `result'
	from -e:16:in `<main>'
#<Thread:0x00007ff89c16d108@/Users/me/.rvm/gems/ruby-2.5.1/gems/sshkit-1.18.0/lib/sshkit/runners/parallel.rb:10 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
	1: from /Users/me/.rvm/gems/ruby-2.5.1/gems/sshkit-1.18.0/lib/sshkit/runners/parallel.rb:11:in `block (2 levels) in execute'
/Users/me/.rvm/gems/ruby-2.5.1/gems/sshkit-1.18.0/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as my_user@:my_path Exception while executing as my_user@my_path: bundle exit status: 1 (SSHKit::Runner::ExecuteError)
bundle stdout: Nothing written
bundle stderr: (erb):28:in `<main>': uninitialized constant Rails (NameError)
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `eval'
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `result'
	from -e:16:in `<main>'
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as my_user@my_path: Exception while executing as my_user@my_path: bundle exit status: 1
bundle stdout: Nothing written
bundle stderr: (erb):28:in `<main>': uninitialized constant Rails (NameError)
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `eval'
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `result'
	from -e:16:in `<main>'


Caused by:
SSHKit::Runner::ExecuteError: Exception while executing as my_user@my_path: bundle exit status: 1
bundle stdout: Nothing written
bundle stderr: (erb):28:in `<main>': uninitialized constant Rails (NameError)
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `eval'
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `result'
	from -e:16:in `<main>'


Caused by:
SSHKit::Command::Failed: bundle exit status: 1
bundle stdout: Nothing written
bundle stderr: (erb):28:in `<main>': uninitialized constant Rails (NameError)
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `eval'
	from /usr/share/rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/erb.rb:876:in `result'
	from -e:16:in `<main>'

Tasks: TOP => postgres:backup:create
(See full trace by running task with --trace)

I believe the key error here is: uninitialized constant Rails (NameError). When I inspect this gem's lib/capistrano3/tasks/postgres.rb file, line 162 is the culprit:

# Grabs remote database config before creating dump <---- line 152
  def grab_remote_database_config
    return if fetch(:postgres_remote_database_config)
    on roles(fetch(:postgres_role)) do |role|
      within release_path do
        env = fetch(:postgres_env).to_s.downcase
        filename = "#{deploy_to}/current/config/database.yml"
        eval_yaml_with_erb = <<-RUBY.strip
          #{env_variables_loader_code(env)}
          require 'erb'
          puts ERB.new(File.read('#{filename}')).result <------- line 162
        RUBY
        capture_config_cmd = "ruby -e \"#{eval_yaml_with_erb}\""
        yaml_content = test('ruby -v') ? capture(capture_config_cmd) : capture(:bundle, :exec, capture_config_cmd)
        set :postgres_remote_database_config,  database_config_defaults.merge(YAML::load(yaml_content)[env])
      end
    end
  end <------- line 169

Because the scope of line 162 has no way to interpret a call to Rails, my database.yml causes this code to fail.

One obvious, easy work around is to use one of the environment gems that this gem supports. Another solution is to update this gem to support calls to Rails -- something like:

cmd = "cd #{current_path} && /home/my_user/.rvm/gems/ruby-2.5.1/wrappers/rails runner -e production 'puts ERB.new(File.read(\"#{filename}\")).result'"

yaml_content = capture(cmd)
loaded = YAML::load(yaml_content)

I am not proficient enough with Capistrano's SSHKit DSL to have figured out a cleaner way to call rails runner, but this is nearly exactly my development code at the moment and its working. A similar change would have to be made to def grab_local_database_config

What are your thoughts on whether this gem should support calls to Rails.application.credentials in the database.yml file?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions