diff --git a/manifests/server/service.pp b/manifests/server/service.pp index ba69fb2..2a4f500 100644 --- a/manifests/server/service.pp +++ b/manifests/server/service.pp @@ -1,48 +1,48 @@ # PRIVATE CLASS: do not call directly class postgresql::server::service { $service_ensure = $postgresql::server::service_ensure $service_enable = $postgresql::server::service_enable $service_manage = $postgresql::server::service_manage $service_name = $postgresql::server::service_name $service_provider = $postgresql::server::service_provider $service_status = $postgresql::server::service_status $user = $postgresql::server::user $port = $postgresql::server::port $default_database = $postgresql::server::default_database $psql_path = $postgresql::params::psql_path anchor { 'postgresql::server::service::begin': } if $service_manage { service { 'postgresqld': ensure => $service_ensure, enable => $service_enable, name => $service_name, provider => $service_provider, hasstatus => true, status => $service_status, } if $service_ensure == 'running' { # This blocks the class before continuing if chained correctly, making # sure the service really is 'up' before continuing. # # Without it, we may continue doing more work before the database is # prepared leading to a nasty race condition. postgresql_conn_validator{ 'validate_service_is_running': - run_as => $user, - db_name => $default_database, - port => $port, - sleep => 1, - tries => 60, - psql_path => $psql_path, - require => Service['postgresqld'], - before => Anchor['postgresql::server::service::end'] + run_as => $user, + db_name => $default_database, + port => $port, + sleep => 1, + tries => 60, + psql_path => $psql_path, + require => Service['postgresqld'], + before => Anchor['postgresql::server::service::end'] } Postgresql::Server::Database <| title == $default_database |> -> Postgresql_conn_validator['validate_service_is_running'] } } anchor { 'postgresql::server::service::end': } } diff --git a/manifests/validate_db_connection.pp b/manifests/validate_db_connection.pp index c678197..37fea72 100644 --- a/manifests/validate_db_connection.pp +++ b/manifests/validate_db_connection.pp @@ -1,97 +1,97 @@ # This type validates that a successful postgres connection can be established # between the node on which this resource is run and a specified postgres # instance (host/port/user/password/database name). # # See README.md for more details. define postgresql::validate_db_connection( $database_host = undef, $database_name = undef, $database_password = undef, $database_username = undef, $database_port = undef, $connect_settings = undef, $run_as = undef, $sleep = 2, $tries = 10, $create_db_first = true ) { include postgresql::client include postgresql::params - warning("postgresql::validate_db_connection is deprecated, please use postgresql_conn_validator.") + warning('postgresql::validate_db_connection is deprecated, please use postgresql_conn_validator.') $psql_path = $postgresql::params::psql_path $module_workdir = $postgresql::params::module_workdir $validcon_script_path = $postgresql::client::validcon_script_path $cmd_init = "${psql_path} --tuples-only --quiet " $cmd_host = $database_host ? { undef => '', default => "-h ${database_host} ", } $cmd_user = $database_username ? { undef => '', default => "-U ${database_username} ", } $cmd_port = $database_port ? { undef => '', default => "-p ${database_port} ", } $cmd_dbname = $database_name ? { undef => "--dbname ${postgresql::params::default_database} ", default => "--dbname ${database_name} ", } $pass_env = $database_password ? { undef => undef, default => "PGPASSWORD=${database_password}", } $cmd = join([$cmd_init, $cmd_host, $cmd_user, $cmd_port, $cmd_dbname], ' ') $validate_cmd = "${validcon_script_path} ${sleep} ${tries} '${cmd}'" # This is more of a safety valve, we add a little extra to compensate for the # time it takes to run each psql command. $timeout = (($sleep + 2) * $tries) # Combine $database_password and $connect_settings into an array of environment # variables, ensure $database_password is last, allowing it to override a password # from the $connect_settings hash if $connect_settings != undef { if $pass_env != undef { $env = concat(join_keys_to_values( $connect_settings, '='), $pass_env) } else { $env = join_keys_to_values( $connect_settings, '=') } } else { $env = $pass_env } warning($validate_cmd) warning($env) $exec_name = "validate postgres connection for ${database_username}@${database_host}:${database_port}/${database_name}" exec { $exec_name: command => "echo 'Unable to connect to defined database using: ${cmd}' && false", unless => $validate_cmd, cwd => $module_workdir, environment => $env, logoutput => 'on_failure', user => $run_as, path => '/bin:/usr/bin:/usr/local/bin', timeout => $timeout, require => Class['postgresql::client'], } # This is a little bit of puppet magic. What we want to do here is make # sure that if the validation and the database instance creation are being # applied on the same machine, then the database resource is applied *before* # the validation resource. Otherwise, the validation is guaranteed to fail # on the first run. # # We accomplish this by using Puppet's resource collection syntax to search # for the Database resource in our current catalog; if it exists, the # appropriate relationship is created here. if($create_db_first) { Postgresql::Server::Database<|title == $database_name|> -> Exec[$exec_name] } } diff --git a/spec/acceptance/db_spec.rb b/spec/acceptance/db_spec.rb index 0279d30..0285c3f 100644 --- a/spec/acceptance/db_spec.rb +++ b/spec/acceptance/db_spec.rb @@ -1,55 +1,54 @@ require 'spec_helper_acceptance' describe 'postgresql::server::db', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do it 'creates a database' do begin tmpdir = default.tmpdir('postgresql') pp = <<-EOS class { 'postgresql::server': postgres_password => 'space password', } postgresql::server::tablespace { 'postgresql-test-db': location => '#{tmpdir}', } -> postgresql::server::db { 'postgresql-test-db': comment => 'testcomment', user => 'test-user', password => 'test1', tablespace => 'postgresql-test-db', } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) - # Verify that the postgres password works shell("echo 'localhost:*:*:postgres:\'space password\'' > /root/.pgpass") shell("chmod 600 /root/.pgpass") shell("psql -U postgres -h localhost --command='\\l'") psql('--command="select datname from pg_database" "postgresql-test-db"') do |r| expect(r.stdout).to match(/postgresql-test-db/) expect(r.stderr).to eq('') end psql('--command="SELECT 1 FROM pg_roles WHERE rolname=\'test-user\'"') do |r| expect(r.stdout).to match(/\(1 row\)/) end result = shell('psql --version') version = result.stdout.match(%r{\s(\d\.\d)})[1] if version > "8.1" comment_information_function = "shobj_description" else comment_information_function = "obj_description" end psql("--dbname postgresql-test-db --command=\"SELECT pg_catalog.#{comment_information_function}(d.oid, 'pg_database') FROM pg_catalog.pg_database d WHERE datname = 'postgresql-test-db' AND pg_catalog.#{comment_information_function}(d.oid, 'pg_database') = 'testcomment'\"") do |r| expect(r.stdout).to match(/\(1 row\)/) end ensure psql('--command=\'drop database "postgresql-test-db" postgres\'') psql('--command="DROP USER test"') end end end diff --git a/spec/acceptance/postgresql_conn_validator_spec.rb b/spec/acceptance/postgresql_conn_validator_spec.rb index 329abc8..d21d431 100644 --- a/spec/acceptance/postgresql_conn_validator_spec.rb +++ b/spec/acceptance/postgresql_conn_validator_spec.rb @@ -1,76 +1,76 @@ require 'spec_helper_acceptance' describe 'postgresql_conn_validator', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do let(:install_pp) { <<-EOS class { 'postgresql::server': postgres_password => 'space password', }-> postgresql::server::role { 'testuser': password_hash => postgresql_password('testuser','test1'), }-> postgresql::server::database { 'testdb': - owner => 'testuser', + owner => 'testuser', require => Postgresql::Server::Role['testuser'] }-> postgresql::server::database_grant { 'allow connect for testuser': privilege => 'CONNECT', - db => 'testdb', - role => 'testuser', + db => 'testdb', + role => 'testuser', } EOS } context 'local connection' do it 'validates successfully with defaults' do pp = <<-EOS #{install_pp}-> postgresql_conn_validator { 'validate this': - db_name => 'testdb', + db_name => 'testdb', db_username => 'testuser', db_password => 'test1', - host => 'localhost', - psql_path => '/usr/bin/psql', + host => 'localhost', + psql_path => '/usr/bin/psql', } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) end it 'works with connect settings hash' do pp = <<-EOS #{install_pp}-> postgresql_conn_validator { 'validate this': connect_settings => { 'PGDATABASE' => 'testdb', 'PGPORT' => '5432', 'PGUSER' => 'testuser', 'PGPASSWORD' => 'test1', 'PGHOST' => 'localhost' }, psql_path => '/usr/bin/psql' } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) end it 'fails gracefully' do pp = <<-EOS #{install_pp}-> postgresql_conn_validator { 'validate this': psql_path => '/usr/bin/psql', - tries => 3 + tries => 3 } EOS result = apply_manifest(pp) expect(result.stderr).to match /Unable to connect to PostgreSQL server/ end end end diff --git a/spec/unit/puppet/provider/postgresql_conn_validator/ruby_spec.rb b/spec/unit/puppet/provider/postgresql_conn_validator/ruby_spec.rb index 9b1fa96..9936970 100644 --- a/spec/unit/puppet/provider/postgresql_conn_validator/ruby_spec.rb +++ b/spec/unit/puppet/provider/postgresql_conn_validator/ruby_spec.rb @@ -1,66 +1,66 @@ require 'spec_helper' describe Puppet::Type.type(:postgresql_conn_validator).provider(:ruby) do let(:resource) { Puppet::Type.type(:postgresql_conn_validator).new({ :name => "testname" }.merge attributes) } let(:provider) { resource.provider } let(:attributes) do { - :psql_path => '/usr/bin/psql', - :host => 'db.test.com', - :port => '4444', + :psql_path => '/usr/bin/psql', + :host => 'db.test.com', + :port => '4444', :db_username => 'testuser', :db_password => 'testpass' } end describe '#build_psql_cmd' do it 'contains expected commandline options' do expect(provider.validator.build_psql_cmd).to match /\/usr\/bin\/psql.*-h.*-p.*-U.*/ end end describe '#parse_connect_settings' do it 'returns array if password is present' do expect(provider.validator.parse_connect_settings).to eq(['PGPASSWORD=testpass']) end it 'returns an empty array if password is nil' do attributes.delete(:db_password) expect(provider.validator.parse_connect_settings).to eq([]) end let(:connect_settings) do { :connect_settings => { :PGPASSWORD => 'testpass', - :PGHOST => 'db.test.com', - :PGPORT => '1234' + :PGHOST => 'db.test.com', + :PGPORT => '1234' } } end it 'returns an array of settings' do attributes.delete(:db_password) attributes.merge! connect_settings expect(provider.validator.parse_connect_settings).to eq(['PGPASSWORD=testpass','PGHOST=db.test.com','PGPORT=1234']) end end describe '#attempt_connection' do let(:sleep_length) {1} let(:tries) {3} let(:exec) { provider.validator.stub(:execute_command).and_return(true) } it 'tries the correct number of times' do expect(provider.validator).to receive(:execute_command).exactly(3).times provider.validator.attempt_connection(sleep_length,tries) end end end