From 8fad79f2bc84d50ca86a831b8d97524983c6e0eb Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sat, 24 Aug 2024 00:30:47 +1000 Subject: [PATCH] feat: manage database/user/grants for patroni - add defines for exporting/collecting psql objects for patroni - add generic profile for managing patroni psql databases for an app --- site/profiles/manifests/sql/patroni.pp | 10 +++ site/profiles/manifests/sql/postgres/db.pp | 9 +++ site/profiles/manifests/sql/postgres/grant.pp | 38 ++++++++++++ site/profiles/manifests/sql/postgres/user.pp | 9 +++ site/profiles/manifests/sql/postgresdb.pp | 61 +++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 site/profiles/manifests/sql/postgres/db.pp create mode 100644 site/profiles/manifests/sql/postgres/grant.pp create mode 100644 site/profiles/manifests/sql/postgres/user.pp create mode 100644 site/profiles/manifests/sql/postgresdb.pp diff --git a/site/profiles/manifests/sql/patroni.pp b/site/profiles/manifests/sql/patroni.pp index 69449cb..e53dcc4 100644 --- a/site/profiles/manifests/sql/patroni.pp +++ b/site/profiles/manifests/sql/patroni.pp @@ -84,6 +84,16 @@ class profiles::sql::patroni ( ], } + $connect_settings = { + + } + + # collect exported resources + $tag = "${facts['country']}-${facts['region']}-${facts['environment']}" + Profiles::Sql::Postgres::Db <<| tag == $tag |>> {} + Profiles::Sql::Postgres::User <<| tag == $tag |>> {} + Profiles::Sql::Postgres::Grant <<| tag == $tag |>> {} + if $postgres_exporter_enabled { class { 'prometheus::postgres_exporter': postgres_user => $postgres_exporter_user, diff --git a/site/profiles/manifests/sql/postgres/db.pp b/site/profiles/manifests/sql/postgres/db.pp new file mode 100644 index 0000000..586820e --- /dev/null +++ b/site/profiles/manifests/sql/postgres/db.pp @@ -0,0 +1,9 @@ +define profiles::sql::postgres::db ( + String $dbname, +) { + postgresql_psql { "create_database_${dbname}": + command => "CREATE DATABASE \"${dbname}\"", + unless => "SELECT 1 FROM pg_database WHERE datname = '${dbname}'", + } +} + diff --git a/site/profiles/manifests/sql/postgres/grant.pp b/site/profiles/manifests/sql/postgres/grant.pp new file mode 100644 index 0000000..2f8476e --- /dev/null +++ b/site/profiles/manifests/sql/postgres/grant.pp @@ -0,0 +1,38 @@ +define profiles::sql::postgres::grant ( + String $username, + Enum['SCHEMA', 'DATABASE'] $type = 'DATABASE', + Optional[String] $dbname = undef, + Optional[String] $schema = undef, + String $privilege = 'ALL PRIVILEGES', +) { + # Validate parameters based on type + if $type == 'DATABASE' and $dbname == undef { + fail('The dbname parameter must be provided when type is DATABASE') + } + + if $type == 'SCHEMA' and ($dbname == undef or $schema == undef) { + fail('Both dbname and schema parameters must be provided when type is SCHEMA') + } + + # Determine the appropriate SQL command and unless condition + $command = $type ? { + 'DATABASE' => "GRANT ${privilege} ON DATABASE ${dbname} TO ${username}", + 'SCHEMA' => "GRANT ${privilege} ON SCHEMA ${schema} TO ${username}", + } + + $unless = $type ? { + 'DATABASE' => "SELECT 1 FROM pg_roles r WHERE r.rolname='${username}' AND has_database_privilege('${username}', '${dbname}', 'CONNECT')", # lint:ignore:140chars + 'SCHEMA' => "SELECT 1 FROM pg_namespace n JOIN pg_roles r ON r.oid = n.nspowner WHERE nspname = '${schema}' AND r.rolname = '${username}'", # lint:ignore:140chars + } + # Ensure the db parameter is set correctly when type is SCHEMA + $effective_dbname = $type ? { + 'SCHEMA' => $dbname, + 'DATABASE' => $dbname, + } + + postgresql_psql { "grant_${privilege}_on_${type}_${effective_dbname}_${schema}_to_${username}": + command => $command, + unless => $unless, + db => $effective_dbname, + } +} diff --git a/site/profiles/manifests/sql/postgres/user.pp b/site/profiles/manifests/sql/postgres/user.pp new file mode 100644 index 0000000..b97ba49 --- /dev/null +++ b/site/profiles/manifests/sql/postgres/user.pp @@ -0,0 +1,9 @@ +define profiles::sql::postgres::user ( + String $username, + String $password, +) { + postgresql_psql { "create_user_${username}": + command => "CREATE USER \"${username}\" WITH ENCRYPTED PASSWORD '${password}'", + unless => "SELECT 1 FROM pg_roles WHERE rolname = '${username}'", + } +} diff --git a/site/profiles/manifests/sql/postgresdb.pp b/site/profiles/manifests/sql/postgresdb.pp new file mode 100644 index 0000000..66b5268 --- /dev/null +++ b/site/profiles/manifests/sql/postgresdb.pp @@ -0,0 +1,61 @@ +class profiles::sql::postgresdb ( + String $dbname, + String $dbuser, + String $dbpass, + Boolean $create_host_users = false, + Boolean $members_lookup = false, + String $members_role = undef, + Array $servers = [], +){ + + # if lookup is enabled + if $members_lookup { + + # check that the role is also set + unless !($members_role == undef) { + fail("members_role must be provided for ${title} when members_lookup is True") + } + + # if it is, find hosts, sort them so they dont cause changes every run + $servers_array = sort(query_nodes("enc_role='${members_role}' and region='${facts['region']}'", 'networking.fqdn')) + + # else use provided array from params + }else{ + $servers_array = $servers + } + + $tag = "${facts['country']}-${facts['region']}-${facts['environment']}" + + # only export from the first server in a cluster + if $servers_array[0] == $facts['networking']['fqdn'] { + + # manage the postgres db + @@profiles::sql::postgres::db { "${facts['networking']['fqdn']}_db_${dbname}": + dbname => $dbname, + tag => $tag, + } + + @@profiles::sql::postgres::user { "${facts['networking']['fqdn']}_role_${dbuser}": + username => $dbuser, + password => $dbpass, + tag => $tag, + } + + @@profiles::sql::postgres::grant { "${facts['networking']['fqdn']}_grant_db_${dbuser}_${dbuser}}": + dbname => $dbname, + username => $dbuser, + type => 'DATABASE', + privilege => 'ALL PRIVILEGES', + tag => $tag, + } + + @@profiles::sql::postgres::grant { "${facts['networking']['fqdn']}_grant_schema_${dbuser}_${dbuser}}": + dbname => $dbname, + username => $dbuser, + type => 'SCHEMA', + schema => 'public', + privilege => 'ALL PRIVILEGES', + tag => $tag, + } + } +}