Skip to content

Commit

Permalink
## v6.0.0, 2018-07-09
Browse files Browse the repository at this point in the history
- Fix for pg_without_sudo; Wasn't adding -U to args
- New feature that will ALTER USER/Password with any change to pg_password. Random passwords will cause each cap setup to run the ALTER USER, but that's fine as a user should technically only be using setup initially. It's not that hard to obtain the new password if this happens.
- New redaction for logging of passwords & SSHKIT 1.17.0 in gemspec
- README updates
  • Loading branch information
NorseGaud committed Jul 9, 2018
1 parent 4a5adc9 commit cde3a13
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 29 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

### master

## v6.0.0, 2018-07-09
- Fix for pg_without_sudo; Wasn't adding -U to args
- New feature that will ALTER USER/Password with any change to pg_password. Random passwords will cause each cap setup to run the ALTER USER, but that's fine as a user should technically only be using setup initially. It's not that hard to obtain the new password if this happens.
- New redaction for logging of passwords & SSHKIT 1.17.0 in gemspec
- README updates

## v5.0.1, 2018-06-05
- Quick fix for fetch(:pg_database) on extension adding

Expand Down
28 changes: 15 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Put the following in your application's `Gemfile`:

group :development do
gem 'capistrano', '~> 3.2.0'
gem 'capistrano-postgresql', '~> 5.0.0'
gem 'capistrano-postgresql', '~> 6.0.0'
end

Then:
Expand All @@ -45,42 +45,44 @@ require 'capistrano/postgresql'
You need to include ONLY ONE of the following in your config/deploy/*.rb files:

```
set :pg_password, ENV['DATABASE_USER_PASSWORD']
set :pg_ask_for_password, true
set :pg_generate_random_password, true
set :pg_password, ENV['DATABASE_USER_PASSWORD'] # Example is an ENV value, but you can use a string instead
set :pg_ask_for_password, true # Prompts user for password on execution of setup
set :pg_generate_random_password, true # Generates a random password on each setup
```

##### Execution of `cap ENV setup` will run ALTER USER on pg_username if there is a different password. If you're using :pg_generate_random_password, you'll get a new random password on each run.

Example config:

```
server 'growtrader.dev', user: 'growtrader', roles: %w{app db}
server 'yoursite.net', user: 'growtrader', roles: %w{app db}
set :stage, :development
set :branch, 'development'
# ==================
# Postgresql setup
set :pg_without_sudo, false
set :pg_host, 'growtrader.dev'
set :pg_database, 'growtrader'
set :pg_username, 'growtrader'
set :pg_host, 'db.yoursite.net'
set :pg_database, 'pg_database_name_here'
set :pg_username, 'pg_username_here'
#set :pg_generate_random_password, true
#set :pg_ask_for_password, true
set :pg_password, ENV['GROWTRADER_PGPASS']
set :pg_password, ENV['yoursite_PGPASS']
set :pg_extensions, ['citext','hstore']
set :pg_encoding, 'UTF-8'
set :pg_pool, '100'
```

Finally, to setup the server(s), run:

$ bundle exec cap production setup
$ bundle exec cap development setup

### Requirements

* Be sure to remove `config/database.yml` from your application's version control.
* Your pg_hba.conf must include `local all all trust`
* Your pg_hba.conf must include `local all all trust`. We ssh into the servers to execute psql commands.
* Make sure the `deploy_to` path exists and has the right privileges on your servers. The ~ symbol (i.e. `~/myapp`) is not supported.
* Within your app/config/deploy/{env}.rb files, you need to specify at least one :app and one :db server.
* Within your app/config/deploy/{env}.rb files, you need to specify at least one :app and one :db server (they can be on the same host; `roles: %w{web app db}`)
* If you have multiple :db role hosts, it's necessary to specify `:primary => true` on the end of your primary :db server.
* gem >= 6.0.0 requires SSHKIT >= 1.17.0 as passwords are redacted from logging.

### How it works

Expand Down
1 change: 1 addition & 0 deletions capistrano-postgresql.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ Gem::Specification.new do |gem|
gem.require_paths = ['lib']

gem.add_dependency 'capistrano', '>= 3.0'
gem.add_dependency 'sshkit', '>= 1.17.0' # 1.17.0 required for log/password redaction
gem.add_development_dependency 'rake'
end
11 changes: 3 additions & 8 deletions lib/capistrano/postgresql/helper_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def remove_extensions
end
end

def generate_database_yml_io(password=fetch(:pg_password))
def generate_database_yml_io
StringIO.open do |s|
s.puts "#{fetch(:pg_env)}:"
{
Expand All @@ -29,7 +29,7 @@ def generate_database_yml_io(password=fetch(:pg_password))
database: fetch(:pg_database),
pool: fetch(:pg_pool),
username: fetch(:pg_username),
password: password,
password: fetch(:pg_password),
host: fetch(:pg_host),
socket: fetch(:pg_socket),
port: fetch(:pg_port),
Expand All @@ -45,12 +45,7 @@ def pg_template(update=false,archetype_file=nil)
raise('Regeneration of archetype database.yml need the original file to update from.') if archetype_file.nil?
raise('Cannot update a custom postgresql.yml.erb file.') if File.exists?(config_file) # Skip custom postgresql.yml.erb if we're updating. It's not supported
# Update yml file from settings
if fetch(:pg_generate_random_password) || !fetch(:pg_password) # We need to prevent updating the archetype file if we've done a random or "ask"ed password
current_password = archetype_file.split("\n").grep(/password/)[0].split('password:')[1].strip
generate_database_yml_io(current_password)
else
generate_database_yml_io
end
generate_database_yml_io
else
if File.exists?(config_file) # If there is a customized file in your rails app template directory, use it and convert any ERB
StringIO.new ERB.new(File.read(config_file)).result(binding)
Expand Down
17 changes: 13 additions & 4 deletions lib/capistrano/postgresql/psql_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,32 @@ module Postgresql
module PsqlHelpers

def psql(type, database, *args)
cmd = [ :psql, "-d #{database}", *args ]
if fetch(:pg_without_sudo)
args.unshift("-U #{fetch(:pg_system_user)}") # Add the :pg_system_user to psql command since we aren't using sudo anymore
# Add the :pg_system_user to psql command since we aren't using sudo anymore
cmd = [ :psql, "-d #{database}", *args.unshift("-U #{fetch(:pg_system_user)}") ]
else
cmd = [:sudo, "-i -u #{fetch(:pg_system_user)}", *cmd]
end
# Allow us to execute the different sshkit commands
if type == 'test'
test *cmd.flatten
test *cmd
elsif type == 'capture'
capture *cmd
else
execute *cmd.flatten
execute *cmd
end
end

def database_user_exists?
psql 'test', fetch(:pg_system_db),'-tAc', %Q{"SELECT 1 FROM pg_roles WHERE rolname='#{fetch(:pg_username)}';" | grep -q 1}
end

def database_user_password_different?
current_password_md5 = psql 'capture', fetch(:pg_system_db),'-tAc', %Q{"select passwd from pg_shadow WHERE usename='#{fetch(:pg_username)}';"}
new_password_md5 = "md5#{Digest::MD5.hexdigest("#{fetch(:pg_password)}#{fetch(:pg_username)}")}"
current_password_md5 == new_password_md5 ? false : true
end

def database_exists?
psql 'test', fetch(:pg_system_db), '-tAc', %Q{"SELECT 1 FROM pg_database WHERE datname='#{fetch(:pg_database)}';" | grep -q 1}
end
Expand Down
2 changes: 1 addition & 1 deletion lib/capistrano/postgresql/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Capistrano
module Postgresql
VERSION = '5.0.1'
VERSION = '6.0.0'
end
end
10 changes: 7 additions & 3 deletions lib/capistrano/tasks/postgresql.rake
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,16 @@ namespace :postgresql do
end
end

desc 'Create pg_username in database'
desc 'Create or update pg_username in database'
task :create_database_user do
on roles :db do
unless database_user_exists?
# If you use CREATE USER instead of CREATE ROLE the LOGIN right is granted automatically; otherwise you must specify it in the WITH clause of the CREATE statement.
psql 'execute', fetch(:pg_system_db), '-c', %Q{"CREATE USER \\"#{fetch(:pg_username)}\\" PASSWORD '#{fetch(:pg_password)}';"}
psql 'execute', fetch(:pg_system_db), '-c', %Q{"CREATE USER \\"#{fetch(:pg_username)}\\" PASSWORD}, redact("'#{fetch(:pg_password)}'"), %Q{;"}
end
if database_user_password_different?
# Ensure updating the password in your deploy/ENV.rb files updates the user, server side
psql 'execute', fetch(:pg_system_db), '-c', %Q{"ALTER USER \\"#{fetch(:pg_username)}\\" WITH PASSWORD}, redact("'#{fetch(:pg_password)}'"), %Q{;"}
end
end
end
Expand Down Expand Up @@ -140,14 +144,14 @@ namespace :postgresql do
if release_roles(:app).empty?
warn " WARNING: There are no servers in your app/config/deploy/#{fetch(:rails_env)}.rb with a :app role... Skipping Postgresql setup."
else
invoke 'postgresql:remove_app_database_yml_files' # Deletes old yml files from all servers. Allows you to avoid having to manually delete the files on your app servers to get a new pool size for example. Don't touch the archetype file to avoid deleting generated passwords.
if release_roles(:db).empty? # Test to be sure we have a :db role host
warn " WARNING: There is no server in your app/config/deploy/#{fetch(:rails_env)}.rb with a :db role... Skipping Postgresql setup."
elsif !fetch(:pg_password) && !fetch(:pg_generate_random_password) && !fetch(:pg_ask_for_password)
warn " WARNING: There is no :pg_password set in your app/config/deploy/#{fetch(:rails_env)}.rb.\n If you don't wish to set it, 'set :pg_generate_random_password, true' or 'set :pg_ask_for_password, true' are available!"
elsif fetch(:pg_generate_random_password) && fetch(:pg_ask_for_password)
warn " WARNING: You cannot have both :pg_generate_random_password and :pg_ask_for_password enabled in app/config/deploy/#{fetch(:rails_env)}.rb."
else
invoke 'postgresql:remove_app_database_yml_files' # Deletes old yml files from all servers. Allows you to avoid having to manually delete the files on your app servers to get a new pool size for example. Don't touch the archetype file to avoid deleting generated passwords.
invoke 'postgresql:create_database_user'
invoke 'postgresql:create_database'
invoke 'postgresql:add_extensions'
Expand Down

0 comments on commit cde3a13

Please sign in to comment.