(MODULES-661) Remote DB support

Adds connection-settings (for remote DB support) when creating DB resources.

Connection-settings allows a hash of options that can be used
when connecting the a remote DB (such as PGHOST, PGPORT, PGPASSWORD
PGSSLKEY) and a special option DBVERSION indicating the version
of the remote database.

Including
- Puppet updates
- Documentation updates
- RSpec unit test updates
- RSpec acceptance test updates
- Some test coverage for connection-settings
- Working acceptance test...
    Basic vagrant setup:
    * Two boxes, server and client
    * Runs puppet code to on server to setup a postgres server that allows all connections and md5 connections, creates db puppet to look at
    * Runs puppet code on client to make a server that a psql command can be run against puppet db on other server
    * Does some fancy stuff to get the fact of the IP from the first server to connect to
- Backwards compatible, with deprecation warnings around old parameters
This commit is contained in:
Tom Hey 2015-03-05 00:34:52 +00:00 committed by David Schmitt
parent 750a8b65d9
commit 6a29636155
22 changed files with 613 additions and 166 deletions

View file

@ -120,6 +120,43 @@ In this example, you would grant ALL privileges on the test1 database and on the
At this point, you would just need to plunk these database name/username/password values into your PuppetDB config files, and you are good to go.
###Managing remote users, roles and permissions
Remote SQL objects are managed using the same Puppet resources as local SQL objects with the additional of a connect_settings hash. This provides control over how Puppet should connect to the remote Postgres instances and the version that should be used when generating SQL commands.
When provided the connect_settings hash can contain environment variables to control Postgres client connections, such as: PGHOST, PGPORT, PGPASSWORD PGSSLKEY (see http://www.postgresql.org/docs/9.4/static/libpq-envars.html) Additionally the special value of DBVERSION can be provided to specify the target database's version. If the connect_settings hash is omitted or empty then Puppet will connect to the local Postgres instance.
A connect_settings hash can be provided with each of the Puppet resources or a default connect_settings hash can be set in postgresql::globals. Per resource configuration of connect_settings allows for SQL object to be creating on multiple database by multiple users.
$connection_settings_super2 = {
'PGUSER' => "super2",
'PGPASSWORD' => "foobar2",
'PGHOST' => "127.0.0.1",
'PGPORT' => "5432",
'PGDATABASE' => "postgres",
}
include postgresql::server
# Connect with no special settings, i.e domain sockets, user postges
postgresql::server::role{'super2':
password_hash => "foobar2",
superuser => true,
connect_settings => {},
require => [
Class['postgresql::globals'],
Class['postgresql::server::service'],
],
}
# Now using this new user connect via TCP
postgresql::server::database { 'db1':
connect_settings => $connection_settings_super2,
require => Postgresql::Server::Role['super2'],
}
Reference
---------
@ -241,6 +278,7 @@ This setting is used to specify the name of the default database to connect with
Path to the `initdb` command.
####`createdb_path`
**Deprecated**
Path to the `createdb` command.
####`psql_path`
@ -370,6 +408,7 @@ List of strings for access control for connection method, users, databases, IPv6
Path to the `initdb` command.
####`createdb_path`
**Deprecated**
Path to the `createdb` command.
####`psql_path`
@ -539,7 +578,7 @@ Value for the setting.
###Resource: postgresql::server::db
This is a convenience resource that creates a database, user and assigns necessary permissions in one go.
This is a convenience resource that creates a local database, user and assigns necessary permissions in one go.
For example, to create a database called `test1` with a corresponding user of the same name, you can use:
@ -612,6 +651,8 @@ Override the locale during creation of the database. Defaults to the default def
####`istemplate`
Define database as a template. Defaults to `false`.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::server::database\_grant
This defined type manages grant based access privileges for users, wrapping the `postgresql::server::database_grant` for database specific permissions. Consult the PostgreSQL documentation for `grant` for more information.
@ -634,6 +675,8 @@ Database to execute the grant against. This should not ordinarily be changed fro
####`psql_user`
OS user for running `psql`. Defaults to the default user for the module, usually `postgres`.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::server::extension
Manages a postgresql extension.
@ -683,6 +726,9 @@ OS user for running `psql`. Defaults to the default user for the module, usually
####`port`
Port to use when connecting. Default to 'undef' which generally defaults to 5432 depending on your PostgreSQL packaging.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::server::pg\_hba\_rule
This defined type allows you to create an access rule for `pg_hba.conf`. For more details see the [PostgreSQL documentation](http://www.postgresql.org/docs/8.2/static/auth-pg-hba-conf.html).
@ -886,6 +932,9 @@ Specifies how many concurrent connections the role can make. Defaults to `-1` me
####`username`
The username of the role to create, defaults to `namevar`.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::server::schema
This defined type can be used to create a schema. For example:
@ -909,6 +958,9 @@ The default owner of the schema.
####`schema`
Name of the schma. Defaults to `namevar`.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::server::table\_grant
This defined type manages grant based access privileges for users. Consult the PostgreSQL documentation for `grant` for more information.
@ -934,6 +986,8 @@ Database to execute the grant against. This should not ordinarily be changed fro
####`psql_user`
OS user for running `psql`. Defaults to the default user for the module, usually `postgres`.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::server::tablespace
This defined type can be used to create a tablespace. For example:
@ -957,8 +1011,11 @@ The default owner of the tablespace.
####`spcname`
Name of the tablespace. Defaults to `namevar`.
####`connect_settings`
Hash of environment variable used when connecting to a remote server. Defaults to connecting to the local Postgres instance.
###Resource: postgresql::validate\_db\_connection
This resource can be utilised inside composite manifests to validate that a client has a valid connection with a remote PostgreSQL database. It can be ran from any node where the PostgreSQL client software is installed to validate connectivity before commencing other dependent tasks in your Puppet manifests, so it is often used when chained to other tasks such as: starting an application server, performing a database migration.
Example usage:
@ -991,6 +1048,9 @@ Username to connect with. Defaults to 'undef', which when using a unix socket an
####`database_password`
Password to connect with. Can be left blank, but that is not recommended.
####`connect_settings`
Hash of environment variable used when connecting to a remote server, this is an alternative to providing individual parameters (database_host, etc.). If provided the individual parameters take precedence.
####`run_as`
The user to run the `psql` command with for authenticiation. This is important when trying to connect to a database locally using Unix sockets and `ident` authentication. It is not needed for remote testing.

View file

@ -16,19 +16,21 @@ Puppet::Type.type(:postgresql_psql).provide(:ruby) do
command.push("-p", resource[:port]) if resource[:port]
command.push("-t", "-c", '"' + sql.gsub('"', '\"') + '"')
environment = get_environment
if resource[:cwd]
Dir.chdir resource[:cwd] do
run_command(command, resource[:psql_user], resource[:psql_group])
run_command(command, resource[:psql_user], resource[:psql_group], environment)
end
else
run_command(command, resource[:psql_user], resource[:psql_group])
run_command(command, resource[:psql_user], resource[:psql_group], environment)
end
end
private
def get_environment
environment = {}
environment = resource[:connect_settings] || {}
if envlist = resource[:environment]
envlist = [envlist] unless envlist.is_a? Array
envlist.each do |setting|
@ -47,7 +49,7 @@ Puppet::Type.type(:postgresql_psql).provide(:ruby) do
return environment
end
def run_command(command, user, group)
def run_command(command, user, group, environment)
command = command.join ' '
environment = get_environment
if Puppet::PUPPETVERSION.to_f < 3.0
@ -66,7 +68,7 @@ Puppet::Type.type(:postgresql_psql).provide(:ruby) do
:failonfail => false,
:combine => true,
:override_locale => true,
:custom_environment => environment
:custom_environment => environment,
})
[output, $CHILD_STATUS.dup]
end

View file

@ -62,12 +62,16 @@ Puppet::Type.newtype(:postgresql_psql) do
end
end
newparam(:connect_settings) do
desc "Connection settings that will be used when connecting to postgres"
end
newparam(:db) do
desc "The name of the database to execute the SQL command against."
desc "The name of the database to execute the SQL command against, this overrides any PGDATABASE value in connect_settings"
end
newparam(:port) do
desc "The port of the database server to execute the SQL command against."
desc "The port of the database server to execute the SQL command against, this overrides any PGPORT value in connect_settings."
end
newparam(:search_path) do

View file

@ -1,58 +1,59 @@
# Class for setting cross-class global overrides. See README.md for more
# details.
class postgresql::globals (
$client_package_name = undef,
$server_package_name = undef,
$contrib_package_name = undef,
$devel_package_name = undef,
$java_package_name = undef,
$docs_package_name = undef,
$perl_package_name = undef,
$plperl_package_name = undef,
$plpython_package_name = undef,
$python_package_name = undef,
$postgis_package_name = undef,
$client_package_name = undef,
$server_package_name = undef,
$contrib_package_name = undef,
$devel_package_name = undef,
$java_package_name = undef,
$docs_package_name = undef,
$perl_package_name = undef,
$plperl_package_name = undef,
$plpython_package_name = undef,
$python_package_name = undef,
$postgis_package_name = undef,
$service_name = undef,
$service_provider = undef,
$service_status = undef,
$default_database = undef,
$service_name = undef,
$service_provider = undef,
$service_status = undef,
$default_database = undef,
$validcon_script_path = undef,
$validcon_script_path = undef,
$initdb_path = undef,
$createdb_path = undef,
$psql_path = undef,
$pg_hba_conf_path = undef,
$pg_ident_conf_path = undef,
$postgresql_conf_path = undef,
$recovery_conf_path = undef,
$initdb_path = undef,
$createdb_path = undef,
$psql_path = undef,
$pg_hba_conf_path = undef,
$pg_ident_conf_path = undef,
$postgresql_conf_path = undef,
$recovery_conf_path = undef,
$default_connect_settings = undef,
$pg_hba_conf_defaults = undef,
$pg_hba_conf_defaults = undef,
$datadir = undef,
$confdir = undef,
$bindir = undef,
$xlogdir = undef,
$logdir = undef,
$datadir = undef,
$confdir = undef,
$bindir = undef,
$xlogdir = undef,
$logdir = undef,
$user = undef,
$group = undef,
$user = undef,
$group = undef,
$version = undef,
$postgis_version = undef,
$repo_proxy = undef,
$version = undef,
$postgis_version = undef,
$repo_proxy = undef,
$needs_initdb = undef,
$needs_initdb = undef,
$encoding = undef,
$locale = undef,
$encoding = undef,
$locale = undef,
$manage_pg_hba_conf = undef,
$manage_pg_ident_conf = undef,
$manage_recovery_conf = undef,
$manage_pg_hba_conf = undef,
$manage_pg_ident_conf = undef,
$manage_recovery_conf = undef,
$manage_package_repo = undef,
$manage_package_repo = undef,
) {
# We are determining this here, because it is needed by the package repo
# class.

View file

@ -253,7 +253,6 @@ class postgresql::params inherits postgresql::globals {
$validcon_script_path = pick($validcon_script_path, '/usr/local/bin/validate_postgresql_connection.sh')
$initdb_path = pick($initdb_path, "${bindir}/initdb")
$createdb_path = pick($createdb_path, "${bindir}/createdb")
$pg_hba_conf_path = pick($pg_hba_conf_path, "${confdir}/pg_hba.conf")
$pg_hba_conf_defaults = pick($pg_hba_conf_defaults, true)
$pg_ident_conf_path = pick($pg_ident_conf_path, "${confdir}/pg_ident.conf")

View file

@ -63,6 +63,10 @@ class postgresql::server (
$_version = $postgresql::params::version
}
if $createdb_path != undef{
warning('Passing "createdb_path" to postgresql::server is deprecated, it can be removed safely for the same behaviour')
}
# Reload has its own ordering, specified by other defines
class { "${pg}::reload": require => Class["${pg}::install"] }

View file

@ -1,28 +1,43 @@
# Define for creating a database. See README.md for more details.
define postgresql::server::database(
$comment = undef,
$dbname = $title,
$owner = $postgresql::server::user,
$tablespace = undef,
$template = 'template0',
$encoding = $postgresql::server::encoding,
$locale = $postgresql::server::locale,
$istemplate = false
$comment = undef,
$dbname = $title,
$owner = $postgresql::server::user,
$tablespace = undef,
$template = 'template0',
$encoding = $postgresql::server::encoding,
$locale = $postgresql::server::locale,
$istemplate = false,
$connect_settings = $postgresql::server::default_connect_settings,
) {
$createdb_path = $postgresql::server::createdb_path
$user = $postgresql::server::user
$group = $postgresql::server::group
$psql_path = $postgresql::server::psql_path
$port = $postgresql::server::port
$version = $postgresql::server::_version
$default_db = $postgresql::server::default_database
# If possible use the version of the remote database, otherwise
# fallback to our local DB version
if $connect_settings != undef and has_key( $connect_settings, 'DBVERSION') {
$version = $connect_settings['DBVERSION']
} else {
$version = $postgresql::server::_version
}
# If the connection settings do not contain a port, then use the local server port
if $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
$port = undef
} else {
$port = $postgresql::server::port
}
# Set the defaults for the postgresql_psql resource
Postgresql_psql {
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
port => $port,
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
port => $port,
connect_settings => $connect_settings,
}
# Optionally set the locale switch. Older versions of createdb may not accept
@ -30,7 +45,7 @@ define postgresql::server::database(
if ($version != '8.1') {
$locale_option = $locale ? {
undef => '',
default => "--locale=${locale} ",
default => "LC_COLLATE=${locale} LC_CTYPE=${locale}",
}
$public_revoke_privilege = 'CONNECT'
} else {
@ -38,40 +53,40 @@ define postgresql::server::database(
$public_revoke_privilege = 'ALL'
}
$template_option = $template ? {
undef => '',
default => "TEMPLATE=${template}",
}
$encoding_option = $encoding ? {
undef => '',
default => "--encoding '${encoding}' ",
default => "ENCODING=${encoding}",
}
$tablespace_option = $tablespace ? {
undef => '',
default => "--tablespace='${tablespace}' ",
default => "TABLESPACE=${tablespace}",
}
$createdb_command = "${createdb_path} --port='${port}' --owner='${owner}' --template=${template} ${encoding_option}${locale_option}${tablespace_option} '${dbname}'"
if $createdb_path != undef{
warning('Passing "createdb_path" to postgresql::database is deprecated, it can be removed safely for the same behaviour')
}
postgresql_psql { "Check for existence of db '${dbname}'":
command => 'SELECT 1',
postgresql_psql { "Create db '${dbname}'":
command => "CREATE DATABASE ${dbname} WITH OWNER=${owner} ${template_option} ${encoding_option} ${locale_option} ${tablespace_option}",
unless => "SELECT datname FROM pg_database WHERE datname='${dbname}'",
db => $default_db,
port => $port,
require => Class['postgresql::server::service']
}~>
exec { $createdb_command :
refreshonly => true,
user => $user,
logoutput => on_failure,
}~>
# This will prevent users from connecting to the database unless they've been
# granted privileges.
postgresql_psql {"REVOKE ${public_revoke_privilege} ON DATABASE \"${dbname}\" FROM public":
db => $default_db,
port => $port,
refreshonly => true,
}
Exec[ $createdb_command ]->
Postgresql_psql[ "Create db '${dbname}'" ]->
postgresql_psql {"UPDATE pg_database SET datistemplate = ${istemplate} WHERE datname = '${dbname}'":
unless => "SELECT datname FROM pg_database WHERE datname = '${dbname}' AND datistemplate = ${istemplate}",
db => $default_db,
@ -83,7 +98,7 @@ define postgresql::server::database(
'8.1' => 'obj_description',
default => 'shobj_description',
}
Exec[ $createdb_command ]->
Postgresql_psql[ "Create db '${dbname}'" ]->
postgresql_psql {"COMMENT ON DATABASE ${dbname} IS '${comment}'":
unless => "SELECT pg_catalog.${comment_information_function}(d.oid, 'pg_database') as \"Description\" FROM pg_catalog.pg_database d WHERE datname = '${dbname}' AND pg_catalog.${comment_information_function}(d.oid, 'pg_database') = '${comment}'",
db => $dbname,
@ -92,6 +107,6 @@ define postgresql::server::database(
# Build up dependencies on tablespace
if($tablespace != undef and defined(Postgresql::Server::Tablespace[$tablespace])) {
Postgresql::Server::Tablespace[$tablespace]->Exec[$createdb_command]
Postgresql::Server::Tablespace[$tablespace]->Postgresql_psql[ "Create db '${dbname}'" ]
}
}

View file

@ -3,16 +3,18 @@ define postgresql::server::database_grant(
$privilege,
$db,
$role,
$psql_db = undef,
$psql_user = undef
$psql_db = undef,
$psql_user = undef,
$connect_settings = undef,
) {
postgresql::server::grant { "database:${name}":
role => $role,
db => $db,
privilege => $privilege,
object_type => 'DATABASE',
object_name => $db,
psql_db => $psql_db,
psql_user => $psql_user,
role => $role,
db => $db,
privilege => $privilege,
object_type => 'DATABASE',
object_name => $db,
psql_db => $psql_db,
psql_user => $psql_user,
connect_settings => $connect_settings,
}
}

View file

@ -4,19 +4,11 @@ define postgresql::server::extension (
$ensure = 'present',
$package_name = undef,
$package_ensure = undef,
$connect_settings = $postgresql::server::default_connect_settings,
) {
$user = $postgresql::server::user
$group = $postgresql::server::group
$psql_path = $postgresql::server::psql_path
$port = $postgresql::server::port
# Set the defaults for the postgresql_psql resource
Postgresql_psql {
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
port => $port,
}
case $ensure {
'present': {
@ -39,10 +31,16 @@ define postgresql::server::extension (
}
postgresql_psql {"Add ${title} extension to ${database}":
db => $database,
command => $command,
unless => "SELECT t.count FROM (SELECT count(extname) FROM pg_extension WHERE extname = '${name}') as t WHERE t.count ${unless_comp} 1",
require => Postgresql::Server::Database[$database],
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
connect_settings => $connect_settings,
db => $database,
command => $command,
unless => "SELECT t.count FROM (SELECT count(extname) FROM pg_extension WHERE extname = '${name}') as t WHERE t.count ${unless_comp} 1",
require => Postgresql::Server::Database[$database],
}
if $package_name {

View file

@ -2,13 +2,14 @@
define postgresql::server::grant (
$role,
$db,
$privilege = undef,
$object_type = 'database',
$object_name = undef,
$psql_db = $postgresql::server::default_database,
$psql_user = $postgresql::server::user,
$port = $postgresql::server::port,
$onlyif_exists = false,
$privilege = undef,
$object_type = 'database',
$object_name = undef,
$psql_db = $postgresql::server::default_database,
$psql_user = $postgresql::server::user,
$port = $postgresql::server::port,
$onlyif_exists = false,
$connect_settings = $postgresql::server::default_connect_settings,
) {
$group = $postgresql::server::group
$psql_path = $postgresql::server::psql_path
@ -20,6 +21,16 @@ define postgresql::server::grant (
}
validate_bool($onlyif_exists)
#
# Port, order of precedence: $port parameter, $connect_settings[PGPORT], $postgresql::server::port
#
if $port != undef {
$port_override = $port
} elsif $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
$port_override = undef
} else {
$port_override = $postgresql::server::port
}
## Munge the input values
$_object_type = upcase($object_type)
@ -217,15 +228,16 @@ define postgresql::server::grant (
$grant_cmd = "GRANT ${_privilege} ON ${_object_type} \"${_togrant_object}\" TO
\"${role}\""
postgresql_psql { "grant:${name}":
command => $grant_cmd,
db => $on_db,
port => $port,
psql_user => $psql_user,
psql_group => $group,
psql_path => $psql_path,
unless => $_unless,
onlyif => $_onlyif,
require => Class['postgresql::server']
command => $grant_cmd,
db => $on_db,
port => $port_override,
connect_settings => $connect_settings,
psql_user => $psql_user,
psql_group => $group,
psql_path => $psql_path,
unless => $_unless,
onlyif => $_onlyif,
require => Class['postgresql::server']
}
if($role != undef and defined(Postgresql::Server::Role[$role])) {
@ -235,4 +247,4 @@ define postgresql::server::grant (
if($db != undef and defined(Postgresql::Server::Database[$db])) {
Postgresql::Server::Database[$db]->Postgresql_psql["grant:${name}"]
}
}
}

View file

@ -4,18 +4,37 @@ define postgresql::server::role(
$createdb = false,
$createrole = false,
$db = $postgresql::server::default_database,
$port = $postgresql::server::port,
$port = undef,
$login = true,
$inherit = true,
$superuser = false,
$replication = false,
$connection_limit = '-1',
$username = $title
$username = $title,
$connect_settings = $postgresql::server::default_connect_settings,
) {
$psql_user = $postgresql::server::user
$psql_group = $postgresql::server::group
$psql_path = $postgresql::server::psql_path
$version = $postgresql::server::_version
#
# Port, order of precedence: $port parameter, $connect_settings[PGPORT], $postgresql::server::port
#
if $port != undef {
$port_override = $port
} elsif $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
$port_override = undef
} else {
$port_override = $postgresql::server::port
}
# If possible use the version of the remote database, otherwise
# fallback to our local DB version
if $connect_settings != undef and has_key( $connect_settings, 'DBVERSION') {
$version = $connect_settings['DBVERSION']
} else {
$version = $postgresql::server::_version
}
$login_sql = $login ? { true => 'LOGIN', default => 'NOLOGIN' }
$inherit_sql = $inherit ? { true => 'INHERIT', default => 'NOINHERIT' }
@ -33,10 +52,11 @@ define postgresql::server::role(
Postgresql_psql {
db => $db,
port => $port,
port => $port_override,
psql_user => $psql_user,
psql_group => $psql_group,
psql_path => $psql_path,
connect_settings => $connect_settings,
require => [
Postgresql_psql["CREATE ROLE ${username} ENCRYPTED PASSWORD ****"],
Class['postgresql::server'],

View file

@ -13,22 +13,30 @@
# }
#
define postgresql::server::schema(
$db,
$db = $postgresql::server::default_database,
$owner = undef,
$schema = $title,
$connect_settings = $postgresql::server::default_connect_settings,
) {
$user = $postgresql::server::user
$group = $postgresql::server::group
$port = $postgresql::server::port
$psql_path = $postgresql::server::psql_path
$version = $postgresql::server::_version
# If the connection settings do not contain a port, then use the local server port
if $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
$port = undef
} else {
$port = $postgresql::server::port
}
Postgresql_psql {
db => $db,
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
port => $port,
connect_settings => $connect_settings,
}
$schema_title = "Create Schema '${title}'"

View file

@ -5,20 +5,22 @@ define postgresql::server::table_grant(
$table,
$db,
$role,
$port = $postgresql::server::port,
$psql_db = undef,
$psql_user = undef,
$onlyif_exists = false,
$port = undef,
$psql_db = undef,
$psql_user = undef,
$connect_settings = undef,
$onlyif_exists = false,
) {
postgresql::server::grant { "table:${name}":
role => $role,
db => $db,
port => $port,
privilege => $privilege,
object_type => 'TABLE',
object_name => $table,
psql_db => $psql_db,
psql_user => $psql_user,
onlyif_exists => $onlyif_exists,
role => $role,
db => $db,
port => $port,
privilege => $privilege,
object_type => 'TABLE',
object_name => $table,
psql_db => $psql_db,
psql_user => $psql_user,
onlyif_exists => $onlyif_exists,
connect_settings => $connect_settings,
}
}
}

View file

@ -2,18 +2,26 @@
define postgresql::server::tablespace(
$location,
$owner = undef,
$spcname = $title
$spcname = $title,
$connect_settings = $postgresql::server::default_connect_settings,
) {
$user = $postgresql::server::user
$group = $postgresql::server::group
$port = $postgresql::server::port
$psql_path = $postgresql::server::psql_path
# If the connection settings do not contain a port, then use the local server port
if $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
$port = undef
} else {
$port = $postgresql::server::port
}
Postgresql_psql {
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
port => $port,
psql_user => $user,
psql_group => $group,
psql_path => $psql_path,
port => $port,
connect_settings => $connect_settings,
}
if ($owner == undef) {

View file

@ -9,6 +9,7 @@ define postgresql::validate_db_connection(
$database_password = undef,
$database_username = undef,
$database_port = undef,
$connect_settings = undef,
$run_as = undef,
$sleep = 2,
$tries = 10,
@ -37,7 +38,7 @@ define postgresql::validate_db_connection(
undef => "--dbname ${postgresql::params::default_database} ",
default => "--dbname ${database_name} ",
}
$env = $database_password ? {
$pass_env = $database_password ? {
undef => undef,
default => "PGPASSWORD=${database_password}",
}
@ -48,7 +49,21 @@ define postgresql::validate_db_connection(
# 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
}
$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,

View file

@ -0,0 +1,18 @@
HOSTS:
database:
roles:
- default
- database
platform: el-6-x86_64
box : centos-64-x64-vbox4210-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
hypervisor : vagrant
client:
roles:
- client
platform: el-6-x86_64
box : centos-64-x64-vbox4210-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: foss

View file

@ -0,0 +1,72 @@
require 'spec_helper_acceptance'
describe 'remote-access', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
if hosts.length > 1
describe "configuring multi-node postgresql" do
# Get the database's IP to connect to from the client
let(:database_ip_address) do
hosts_as('database').inject({}) do |memo,host|
fact_on host, "ipaddress_eth1"
end
end
hosts_as('database').each do |host|
it "should be able to configure a host as database on #{host}" do
pp = <<-EOS
# Stop firewall so we can easily connect
service {'iptables':
ensure => 'stopped',
}
class { 'postgresql::server':
ip_mask_allow_all_users => '0.0.0.0/0',
listen_addresses => '*',
}
postgresql::server::db { 'puppet':
user => 'puppet',
password => postgresql_password('puppet', 'puppet'),
}
postgresql::server::pg_hba_rule { 'allow full yolo access password':
type => 'host',
database => 'all',
user => 'all',
address => '0.0.0.0/0',
auth_method => 'password',
order => '002',
}
EOS
apply_manifest_on(host, pp, :catch_failures => true)
end
end
hosts_as('client').each do |host|
it "should be able to configure a host as client on #{host} and then access database" do
pp = <<-EOS
class { 'postgresql::client':}
$connection_settings = {
'PGUSER' => "puppet",
'PGPASSWORD' => "puppet",
'PGHOST' => "#{database_ip_address}",
'PGPORT' => "5432",
'PGDATABASE' => "puppet",
}
postgresql_psql { 'run using connection_settings':
command => 'select 1',
psql_user => 'root',
psql_group => 'root',
connect_settings => $connection_settings,
}
EOS
apply_manifest_on(host, pp, :catch_failures => true)
end
end
end
end
end

View file

@ -21,11 +21,53 @@ describe 'postgresql::server::database', :type => :define do
end
it { is_expected.to contain_postgresql__server__database('test') }
it { is_expected.to contain_postgresql_psql("Check for existence of db 'test'") }
it { is_expected.to contain_postgresql_psql("Create db 'test'") }
context "with comment set to 'test comment'" do
let (:params) {{ :comment => 'test comment' }}
let (:params) {{ :comment => 'test comment',
:connect_settings => {} }}
it { is_expected.to contain_postgresql_psql("COMMENT ON DATABASE test IS 'test comment'").with_connect_settings( {} ) }
end
context "with specific db connection settings - default port" do
let :pre_condition do
"class {'postgresql::server':}"
end
let (:params) {{ :connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1', }}}
it { is_expected.to contain_postgresql_psql("Create db 'test'").with_connect_settings( { 'PGHOST' => 'postgres-db-server','DBVERSION' => '9.1' } ).with_port( 5432 ) }
end
context "with specific db connection settings - including port" do
let :pre_condition do
"class {'postgresql::globals':}
class {'postgresql::server':}"
end
let (:params) {{ :connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGPORT' => '1234' }}}
it { is_expected.to contain_postgresql_psql("Create db 'test'").with_connect_settings( { 'PGHOST' => 'postgres-db-server','DBVERSION' => '9.1','PGPORT' => '1234' } ).with_port( nil ) }
end
context "with global db connection settings - including port" do
let :pre_condition do
"class {'postgresql::globals':
default_connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.2',
'PGPORT' => '1234' }
}
class {'postgresql::server':}"
end
it { is_expected.to contain_postgresql_psql("Create db 'test'").with_connect_settings( { 'PGHOST' => 'postgres-db-server','DBVERSION' => '9.2','PGPORT' => '1234' } ).with_port( nil ) }
it { is_expected.to contain_postgresql_psql("COMMENT ON DATABASE test IS 'test comment'") }
end
end

View file

@ -76,4 +76,61 @@ describe 'postgresql::server::grant', :type => :define do
'unless' => "SELECT 1 FROM (\n SELECT sequence_name\n FROM information_schema.sequences\n WHERE sequence_schema='public'\n EXCEPT DISTINCT\n SELECT object_name as sequence_name\n FROM information_schema.role_usage_grants\n WHERE object_type='SEQUENCE'\n AND grantee='test'\n AND object_schema='public'\n AND privilege_type='USAGE'\n ) P\n HAVING count(P.sequence_name) = 0",
}) }
end
end
context "with specific db connection settings - default port" do
let :params do
{
:db => 'test',
:role => 'test',
:connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1', },
}
end
let :pre_condition do
"class {'postgresql::server':}"
end
it { is_expected.to contain_postgresql__server__grant('test') }
it { is_expected.to contain_postgresql_psql("grant:test").with_connect_settings( { 'PGHOST' => 'postgres-db-server','DBVERSION' => '9.1' } ).with_port( 5432 ) }
end
context "with specific db connection settings - including port" do
let :params do
{
:db => 'test',
:role => 'test',
:connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGPORT' => '1234', },
}
end
let :pre_condition do
"class {'postgresql::server':}"
end
it { is_expected.to contain_postgresql__server__grant('test') }
it { is_expected.to contain_postgresql_psql("grant:test").with_connect_settings( { 'PGHOST' => 'postgres-db-server','DBVERSION' => '9.1','PGPORT' => '1234' } ) }
end
context "with specific db connection settings - port overriden by explicit parameter" do
let :params do
{
:db => 'test',
:role => 'test',
:connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGPORT' => '1234', },
:port => '5678',
}
end
let :pre_condition do
"class {'postgresql::server':}"
end
it { is_expected.to contain_postgresql__server__grant('test') }
it { is_expected.to contain_postgresql_psql("grant:test").with_connect_settings( { 'PGHOST' => 'postgres-db-server','DBVERSION' => '9.1','PGPORT' => '1234' } ).with_port( '5678' ) }
end
end

View file

@ -20,6 +20,7 @@ describe 'postgresql::server::role', :type => :define do
let :params do
{
:password_hash => 'new-pa$s',
:connect_settings => {},
}
end
@ -33,6 +34,8 @@ describe 'postgresql::server::role', :type => :define do
'command' => "CREATE ROLE \"test\" ENCRYPTED PASSWORD '$NEWPGPASSWD' LOGIN NOCREATEROLE NOCREATEDB NOSUPERUSER CONNECTION LIMIT -1",
'environment' => "NEWPGPASSWD=new-pa$s",
'unless' => "SELECT rolname FROM pg_roles WHERE rolname='test'",
'port' => "5432",
'connect_settings' => {},
})
end
it 'should have alter role for "test" user with password as ****' do
@ -40,6 +43,96 @@ describe 'postgresql::server::role', :type => :define do
'command' => "ALTER ROLE \"test\" ENCRYPTED PASSWORD '$NEWPGPASSWD'",
'environment' => "NEWPGPASSWD=new-pa$s",
'unless' => "SELECT usename FROM pg_shadow WHERE usename='test' and passwd='md5b6f7fcbbabb4befde4588a26c1cfd2fa'",
'port' => "5432",
'connect_settings' => {},
})
end
context "with specific db connection settings - default port" do
let :params do
{
:password_hash => 'new-pa$s',
:connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGUSER' => 'login-user',
'PGPASSWORD' => 'login-pass' },
}
end
let :pre_condition do
"class {'postgresql::server':}"
end
it { is_expected.to contain_postgresql__server__role('test') }
it 'should have create role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('CREATE ROLE test ENCRYPTED PASSWORD ****').with({
'command' => "CREATE ROLE \"test\" ENCRYPTED PASSWORD '$NEWPGPASSWD' LOGIN NOCREATEROLE NOCREATEDB NOSUPERUSER CONNECTION LIMIT -1",
'environment' => "NEWPGPASSWD=new-pa$s",
'unless' => "SELECT rolname FROM pg_roles WHERE rolname='test'",
'port' => "5432",
'connect_settings' => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGUSER' => 'login-user',
'PGPASSWORD' => 'login-pass' },
})
end
it 'should have alter role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('ALTER ROLE test ENCRYPTED PASSWORD ****').with({
'command' => "ALTER ROLE \"test\" ENCRYPTED PASSWORD '$NEWPGPASSWD'",
'environment' => "NEWPGPASSWD=new-pa$s",
'unless' => "SELECT usename FROM pg_shadow WHERE usename='test' and passwd='md5b6f7fcbbabb4befde4588a26c1cfd2fa'",
'port' => "5432",
'connect_settings' => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGUSER' => 'login-user',
'PGPASSWORD' => 'login-pass' },
})
end
end
context "with specific db connection settings - including port" do
let :params do
{
:password_hash => 'new-pa$s',
:connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGPORT' => '1234',
'PGUSER' => 'login-user',
'PGPASSWORD' => 'login-pass' },
}
end
let :pre_condition do
"class {'postgresql::server':}"
end
it { is_expected.to contain_postgresql__server__role('test') }
it 'should have create role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('CREATE ROLE test ENCRYPTED PASSWORD ****').with({
'command' => "CREATE ROLE \"test\" ENCRYPTED PASSWORD '$NEWPGPASSWD' LOGIN NOCREATEROLE NOCREATEDB NOSUPERUSER CONNECTION LIMIT -1",
'environment' => "NEWPGPASSWD=new-pa$s",
'unless' => "SELECT rolname FROM pg_roles WHERE rolname='test'",
'connect_settings' => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGPORT' => '1234',
'PGUSER' => 'login-user',
'PGPASSWORD' => 'login-pass' },
})
end
it 'should have alter role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('ALTER ROLE test ENCRYPTED PASSWORD ****').with({
'command' => "ALTER ROLE \"test\" ENCRYPTED PASSWORD '$NEWPGPASSWD'",
'environment' => "NEWPGPASSWD=new-pa$s",
'unless' => "SELECT usename FROM pg_shadow WHERE usename='test' and passwd='md5b6f7fcbbabb4befde4588a26c1cfd2fa'",
'connect_settings' => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1',
'PGPORT' => '1234',
'PGUSER' => 'login-user',
'PGPASSWORD' => 'login-pass' },
})
end
end
end

View file

@ -15,7 +15,7 @@ describe Puppet::Type.type(:postgresql_psql).provider(:ruby) do
it "executes with the given psql_path on the given DB" do
expect(provider).to receive(:run_command).with(['psql', '-d',
attributes[:db], '-t', '-c', '"SELECT \'something\' as \"Custom column\""'], 'postgres',
'postgres')
'postgres', {})
provider.run_sql_command('SELECT \'something\' as "Custom column"')
end
@ -33,7 +33,7 @@ describe Puppet::Type.type(:postgresql_psql).provider(:ruby) do
expect(Dir).to receive(:chdir).with(attributes[:cwd]).and_yield
expect(provider).to receive(:run_command).with([attributes[:psql_path],
'-d', attributes[:db], '-t', '-c', '"SELECT \'something\' as \"Custom column\""'],
attributes[:psql_user], attributes[:psql_group])
attributes[:psql_user], attributes[:psql_group], {})
provider.run_sql_command('SELECT \'something\' as "Custom column"')
end
@ -46,7 +46,7 @@ describe Puppet::Type.type(:postgresql_psql).provider(:ruby) do
it "executes with the given search_path" do
expect(provider).to receive(:run_command).with(['psql', '-t', '-c',
'"set search_path to schema1; SELECT \'something\' as \"Custom column\""'],
'postgres', 'postgres')
'postgres', 'postgres', {})
provider.run_sql_command('SELECT \'something\' as "Custom column"')
end
@ -60,7 +60,8 @@ describe Puppet::Type.type(:postgresql_psql).provider(:ruby) do
expect(provider).to receive(:run_command).with(['psql', '-t', '-c',
'"set search_path to schema1,schema2; SELECT \'something\' as \"Custom column\""'],
'postgres',
'postgres'
'postgres',
{}
)
provider.run_sql_command('SELECT \'something\' as "Custom column"')
@ -74,7 +75,19 @@ describe Puppet::Type.type(:postgresql_psql).provider(:ruby) do
expect(provider).to receive(:run_command).with(["psql",
"-p", "5555",
"-t", "-c", "\"SELECT something\""],
"postgres", "postgres")
"postgres", "postgres", {} )
provider.run_sql_command("SELECT something")
end
end
describe "with connect_settings" do
let(:attributes) do { :connect_settings => { 'PGHOST' => '127.0.0.1' } } end
it "executes with the given host" do
expect(provider).to receive(:run_command).with(["psql",
"-t", "-c",
"\"SELECT something\""],
"postgres", "postgres", { 'PGHOST' => '127.0.0.1' } )
provider.run_sql_command("SELECT something")
end

View file

@ -1,7 +1,7 @@
require 'spec_helper'
describe Puppet::Type.type(:postgresql_psql), "when validating attributes" do
[:name, :unless, :db, :psql_path, :psql_user, :psql_group].each do |attr|
[:name, :unless, :db, :psql_path, :psql_user, :psql_group, :connect_settings].each do |attr|
it "should have a #{attr} parameter" do
expect(Puppet::Type.type(:postgresql_psql).attrtype(attr)).to eq(:param)
end
@ -21,16 +21,18 @@ describe Puppet::Type.type(:postgresql_psql), :unless => Puppet.features.microso
describe "available attributes" do
{
:name => "rspec",
:command => "SELECT stuff",
:unless => "SELECT other,stuff",
:db => "postgres",
:psql_path => "/bin/false",
:psql_user => "postgres",
:psql_group => "postgres",
:cwd => "/var/lib",
:refreshonly => :true,
:search_path => [ "schema1", "schema2"]
:name => "rspec",
:command => "SELECT stuff",
:unless => "SELECT other,stuff",
:db => "postgres",
:psql_path => "/bin/false",
:psql_user => "postgres",
:psql_group => "postgres",
:cwd => "/var/lib",
:refreshonly => :true,
:search_path => [ "schema1", "schema2"],
:connect_settings => { 'PGHOST' => 'postgres-db-server',
'DBVERSION' => '9.1', },
}.each do |attr, value|
context attr do
let(:attributes) do { attr => value } end