diff --git a/README.md b/README.md index bef4b8bac6..2785010a37 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ * [Getting started with postgresql](#getting-started-with-postgresql) 3. [Usage - Configuration options and additional functionality](#usage) * [Configure a server](#configure-a-server) + * [Configure an instance](#configure-an-instance) * [Create a database](#create-a-database) * [Manage users, roles, and permissions](#manage-users-roles-and-permissions) * [Manage ownership of DB objects](#manage-ownership-of-db-objects) @@ -72,6 +73,184 @@ If you get an error message from these commands, your permission settings restri For more details about server configuration parameters, consult the [PostgreSQL Runtime Configuration documentation](http://www.postgresql.org/docs/current/static/runtime-config.html). +### Configure an instance + +This module supports managing multiple instances (the default instance is referred to as 'main' and managed via including the server.pp class) + +**NOTE:** This feature is currently tested on Centos 8 Streams/RHEL8 with DNF Modules enabled. Different Linux plattforms and/or the Postgresql.org +packages distribute different Systemd service files or use wrapper scripts with Systemd to start Postgres. Additional adjustmentments are needed to get this working on these plattforms. + +#### Working Plattforms + +* Centos 8 Streams +* RHEL 8 + +#### Background and example + +creating a new instance has the following advantages: +* files are owned by the postgres user +* instance is running under a different user, if the instance is hacked, the hacker has no access to the file system +* the instance user can be an LDAP user, higher security because of central login monitoring, password policies, password rotation policies +* main instance can be disabled + + +Here is a profile which can be used to create instaces + +```puppet +class profiles::postgres ( + Hash $instances = {}, + String $postgresql_version = '13', +) { + class { 'postgresql::globals': + encoding => 'UTF-8', + locale => 'en_US.UTF-8', + manage_package_repo => false, + manage_dnf_module => true, + needs_initdb => true, + version => $postgresql_version, + } + include postgresql::server + + $instances.each |String $instance, Hash $instance_settings| { + postgresql::server_instance { $instance: + * => $instance_settings, + } + } +} +``` + +And here is data to create an instance called test1: + +```yaml +# stop default main instance +postgresql::server::service_ensure: "stopped" +postgresql::server::service_enable: false + +#define an instance +profiles::postgres::instances: + test1: + instance_user: "ins_test1" + instance_group: "ins_test1" + instance_directories: + "/opt/pgsql": + ensure: directory + "/opt/pgsql/backup": + ensure: directory + "/opt/pgsql/data": + ensure: directory + "/opt/pgsql/data/13": + ensure: directory + "/opt/pgsql/data/home": + ensure: directory + "/opt/pgsql/wal": + ensure: directory + "/opt/pgsql/log": + ensure: directory + "/opt/pgsql/log/13": + ensure: directory + "/opt/pgsql/log/13/test1": + ensure: directory + config_settings: + pg_hba_conf_path: "/opt/pgsql/data/13/test1/pg_hba.conf" + postgresql_conf_path: "/opt/pgsql/data/13/test1/postgresql.conf" + pg_ident_conf_path: "/opt/pgsql/data/13/test1/pg_ident.conf" + datadir: "/opt/pgsql/data/13/test1" + service_name: "postgresql@13-test1" + port: 5433 + pg_hba_conf_defaults: false + service_settings: + service_name: "postgresql@13-test1" + service_status: "systemctl status postgresql@13-test1.service" + service_ensure: "running" + service_enable: true + initdb_settings: + auth_local: "peer" + auth_host: "md5" + needs_initdb: true + datadir: "/opt/pgsql/data/13/test1" + encoding: "UTF-8" + lc_messages: "en_US.UTF8" + locale: "en_US.UTF8" + data_checksums: false + group: "postgres" + user: "postgres" + username: "ins_test1" + config_entries: + authentication_timeout: + value: "1min" + comment: "a test" + log_statement_stats: + value: "off" + autovacuum_vacuum_scale_factor: + value: 0.3 + databases: + testdb1: + encoding: "UTF8" + locale: "en_US.UTF8" + owner: "dba_test1" + testdb2: + encoding: "UTF8" + locale: "en_US.UTF8" + owner: "dba_test1" + roles: + "ins_test1": + superuser: true + login: true + "dba_test1": + createdb: true + login: true + "app_test1": + login: true + "rep_test1": + replication: true + login: true + "rou_test1": + login: true + pg_hba_rules: + "local all INSTANCE user": + type: "local" + database: "all" + user: "ins_test1" + auth_method: "peer" + order: 1 + "local all DB user": + type: "local" + database: "all" + user: "dba_test1" + auth_method: "peer" + order: 2 + "local all APP user": + type: "local" + database: "all" + user: "app_test1" + auth_method: "peer" + order: 3 + "local all READONLY user": + type: "local" + database: "all" + user: "rou_test1" + auth_method: "peer" + order: 4 + "remote all INSTANCE user PGADMIN server": + type: "host" + database: "all" + user: "ins_test1" + address: "192.168.22.131/32" + auth_method: "md5" + order: 5 + "local replication INSTANCE user": + type: "local" + database: "replication" + user: "ins_test1" + auth_method: "peer" + order: 6 + "local replication REPLICATION user": + type: "local" + database: "replication" + user: "rep_test1" + auth_method: "peer" + order: 7 +``` ### Create a database You can set up a variety of PostgreSQL databases with the `postgresql::server::db` defined type. For instance, to set up a database for PuppetDB: @@ -359,7 +538,7 @@ For information on the classes and types, see the [REFERENCE.md](https://github. ## Limitations -Works with versions of PostgreSQL on supported OSes. +Works with versions of PostgreSQL on supported OSes. For an extensive list of supported operating systems, see [metadata.json](https://github.com/puppetlabs/puppetlabs-postgresql/blob/main/metadata.json) diff --git a/REFERENCE.md b/REFERENCE.md index b65c0275e6..207f4ab99a 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -63,6 +63,7 @@ * [`postgresql::server::schema`](#postgresql--server--schema): Create a new schema. * [`postgresql::server::table_grant`](#postgresql--server--table_grant): This resource wraps the grant resource to manage table grants specifically. * [`postgresql::server::tablespace`](#postgresql--server--tablespace): This module creates tablespace. +* [`postgresql::server_instance`](#postgresql--server_instance): define to install and manage additional postgresql instances #### Private Defined types @@ -1402,7 +1403,7 @@ Data type: `Optional[String[1]]` username of user running the postgres instance -Default value: `undef` +Default value: `$user` ### `postgresql::server::contrib` @@ -1526,6 +1527,7 @@ The following parameters are available in the `postgresql::server::config_entry` * [`key`](#-postgresql--server--config_entry--key) * [`value`](#-postgresql--server--config_entry--value) * [`path`](#-postgresql--server--config_entry--path) +* [`comment`](#-postgresql--server--config_entry--comment) ##### `ensure` @@ -1559,6 +1561,14 @@ Path for postgresql.conf Default value: `$postgresql::server::postgresql_conf_path` +##### `comment` + +Data type: `Optional[String[1]]` + +Defines the comment for the setting. The # is added by default. + +Default value: `undef` + ### `postgresql::server::database` Define for creating a database. @@ -1575,6 +1585,7 @@ The following parameters are available in the `postgresql::server::database` def * [`encoding`](#-postgresql--server--database--encoding) * [`locale`](#-postgresql--server--database--locale) * [`istemplate`](#-postgresql--server--database--istemplate) +* [`instance`](#-postgresql--server--database--instance) * [`connect_settings`](#-postgresql--server--database--connect_settings) * [`psql_path`](#-postgresql--server--database--psql_path) * [`default_db`](#-postgresql--server--database--default_db) @@ -1646,6 +1657,14 @@ Defines the database as a template if set to true. Default value: `false` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ##### `connect_settings` Data type: `Hash` @@ -1800,6 +1819,7 @@ The following parameters are available in the `postgresql::server::db` defined t * [`port`](#-postgresql--server--db--port) * [`psql_user`](#-postgresql--server--db--psql_user) * [`psql_group`](#-postgresql--server--db--psql_group) +* [`instance`](#-postgresql--server--db--instance) ##### `user` @@ -1911,6 +1931,14 @@ Overrides the default PostgreSQL user group to be used for related files in the Default value: `$postgresql::server::group` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ### `postgresql::server::default_privileges` Manage a database defaults privileges. Only works with PostgreSQL version 9.6 and above. @@ -1931,6 +1959,7 @@ The following parameters are available in the `postgresql::server::default_privi * [`psql_path`](#-postgresql--server--default_privileges--psql_path) * [`port`](#-postgresql--server--default_privileges--port) * [`connect_settings`](#-postgresql--server--default_privileges--connect_settings) +* [`instance`](#-postgresql--server--default_privileges--instance) * [`group`](#-postgresql--server--default_privileges--group) ##### `target_role` @@ -2032,6 +2061,14 @@ Specifies a hash of environment variables used when connecting to a remote serve Default value: `$postgresql::server::default_connect_settings` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ##### `group` Data type: `String` @@ -2058,6 +2095,7 @@ The following parameters are available in the `postgresql::server::extension` de * [`port`](#-postgresql--server--extension--port) * [`connect_settings`](#-postgresql--server--extension--connect_settings) * [`database_resource_name`](#-postgresql--server--extension--database_resource_name) +* [`instance`](#-postgresql--server--extension--instance) * [`psql_path`](#-postgresql--server--extension--psql_path) * [`user`](#-postgresql--server--extension--user) * [`group`](#-postgresql--server--extension--group) @@ -2148,6 +2186,14 @@ Specifies the resource name of the DB being managed. Defaults to the parameter $ Default value: `$database` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ##### `psql_path` Data type: `Stdlib::Absolutepath` @@ -2194,6 +2240,7 @@ The following parameters are available in the `postgresql::server::grant` define * [`ensure`](#-postgresql--server--grant--ensure) * [`group`](#-postgresql--server--grant--group) * [`psql_path`](#-postgresql--server--grant--psql_path) +* [`instance`](#-postgresql--server--grant--instance) ##### `role` @@ -2323,6 +2370,14 @@ Sets the path to psql command Default value: `$postgresql::server::psql_path` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ### `postgresql::server::grant_role` Define for granting membership to a role. @@ -2338,6 +2393,7 @@ The following parameters are available in the `postgresql::server::grant_role` d * [`psql_user`](#-postgresql--server--grant_role--psql_user) * [`port`](#-postgresql--server--grant_role--port) * [`connect_settings`](#-postgresql--server--grant_role--connect_settings) +* [`instance`](#-postgresql--server--grant_role--instance) ##### `group` @@ -2393,6 +2449,14 @@ Specifies a hash of environment variables used when connecting to a remote serve Default value: `$postgresql::server::default_connect_settings` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ### `postgresql::server::instance::config` Manages the config for a postgresql::server instance @@ -3330,6 +3394,7 @@ The following parameters are available in the `postgresql::server::reassign_owne * [`connect_settings`](#-postgresql--server--reassign_owned_by--connect_settings) * [`group`](#-postgresql--server--reassign_owned_by--group) * [`psql_path`](#-postgresql--server--reassign_owned_by--psql_path) +* [`instance`](#-postgresql--server--reassign_owned_by--instance) ##### `old_role` @@ -3389,6 +3454,14 @@ Sets the path to psql command Default value: `$postgresql::server::psql_path` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ### `postgresql::server::recovery` This resource manages the parameters that applies to the recovery.conf template. @@ -3578,6 +3651,7 @@ The following parameters are available in the `postgresql::server::role` defined * [`module_workdir`](#-postgresql--server--role--module_workdir) * [`hash`](#-postgresql--server--role--hash) * [`salt`](#-postgresql--server--role--salt) +* [`instance`](#-postgresql--server--role--instance) ##### `update_password` @@ -3740,6 +3814,14 @@ Specify the salt use for the scram-sha-256 encoding password (default username) Default value: `undef` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ### `postgresql::server::schema` Create a new schema. @@ -3769,6 +3851,7 @@ The following parameters are available in the `postgresql::server::schema` defin * [`group`](#-postgresql--server--schema--group) * [`psql_path`](#-postgresql--server--schema--psql_path) * [`module_workdir`](#-postgresql--server--schema--module_workdir) +* [`instance`](#-postgresql--server--schema--instance) ##### `db` @@ -3843,6 +3926,14 @@ May need to specify if '/tmp' is on volume mounted with noexec option. Default value: `$postgresql::server::module_workdir` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + ### `postgresql::server::table_grant` This resource wraps the grant resource to manage table grants specifically. @@ -3958,6 +4049,7 @@ The following parameters are available in the `postgresql::server::tablespace` d * [`group`](#-postgresql--server--tablespace--group) * [`psql_path`](#-postgresql--server--tablespace--psql_path) * [`module_workdir`](#-postgresql--server--tablespace--module_workdir) +* [`instance`](#-postgresql--server--tablespace--instance) ##### `location` @@ -4038,6 +4130,176 @@ May need to specify if '/tmp' is on volume mounted with noexec option. Default value: `$postgresql::server::module_workdir` +##### `instance` + +Data type: `String[1]` + +The name of the Postgresql database instance. + +Default value: `'main'` + +### `postgresql::server_instance` + +define to install and manage additional postgresql instances + +#### Parameters + +The following parameters are available in the `postgresql::server_instance` defined type: + +* [`instance_name`](#-postgresql--server_instance--instance_name) +* [`instance_user`](#-postgresql--server_instance--instance_user) +* [`instance_group`](#-postgresql--server_instance--instance_group) +* [`instance_user_homedirectory`](#-postgresql--server_instance--instance_user_homedirectory) +* [`manage_instance_user_and_group`](#-postgresql--server_instance--manage_instance_user_and_group) +* [`instance_directories`](#-postgresql--server_instance--instance_directories) +* [`initdb_settings`](#-postgresql--server_instance--initdb_settings) +* [`config_settings`](#-postgresql--server_instance--config_settings) +* [`service_settings`](#-postgresql--server_instance--service_settings) +* [`passwd_settings`](#-postgresql--server_instance--passwd_settings) +* [`roles`](#-postgresql--server_instance--roles) +* [`config_entries`](#-postgresql--server_instance--config_entries) +* [`pg_hba_rules`](#-postgresql--server_instance--pg_hba_rules) +* [`databases`](#-postgresql--server_instance--databases) +* [`databases_and_users`](#-postgresql--server_instance--databases_and_users) +* [`database_grants`](#-postgresql--server_instance--database_grants) +* [`table_grants`](#-postgresql--server_instance--table_grants) + +##### `instance_name` + +Data type: `String[1]` + +The name of the instance. + +Default value: `$name` + +##### `instance_user` + +Data type: `String[1]` + +The user to run the instance as. + +Default value: `$instance_name` + +##### `instance_group` + +Data type: `String[1]` + +The group to run the instance as. + +Default value: `$instance_name` + +##### `instance_user_homedirectory` + +Data type: `Stdlib::Absolutepath` + +The home directory of the instance user. + +Default value: `"/opt/pgsql/data/home/${instance_user}"` + +##### `manage_instance_user_and_group` + +Data type: `Boolean` + +Should Puppet manage the instance user and it's primary group?. + +Default value: `true` + +##### `instance_directories` + +Data type: `Hash` + +directories needed for the instance. Option to manage the directory properties for each directory. + +Default value: `{}` + +##### `initdb_settings` + +Data type: `Hash` + +Specifies a hash witn parameters for postgresql::server::instance::initdb + +Default value: `{}` + +##### `config_settings` + +Data type: `Hash` + +Specifies a hash with parameters for postgresql::server::instance::config + +Default value: `{}` + +##### `service_settings` + +Data type: `Hash` + +Specifies a hash with parameters for postgresql::server:::instance::service + +Default value: `{}` + +##### `passwd_settings` + +Data type: `Hash` + +Specifies a hash with parameters for postgresql::server::instance::passwd + +Default value: `{}` + +##### `roles` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::role resources. + +Default value: `{}` + +##### `config_entries` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::config_entry resources. + +Default value: `{}` + +##### `pg_hba_rules` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::pg_hba_rule resources. + +Default value: `{}` + +##### `databases` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::database resources. + +Default value: `{}` + +##### `databases_and_users` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::db resources. + +Default value: `{}` + +##### `database_grants` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::database_grant resources. + +Default value: `{}` + +##### `table_grants` + +Data type: `Hash` + +Specifies a hash from which to generate postgresql::server::table_grant resources. + +Default value: `{}` + ## Resource types ### `postgresql_conf` @@ -4048,6 +4310,12 @@ This type allows puppet to manage postgresql.conf parameters. The following properties are available in the `postgresql_conf` type. +##### `comment` + +Valid values: `%r{^[\w\W]+$}` + +The comment to set for this parameter. + ##### `ensure` Valid values: `present`, `absent` @@ -4056,20 +4324,26 @@ The basic property that the resource should be in. Default value: `present` -##### `target` - -The path to postgresql.conf - ##### `value` +Valid values: `%r{^\S(.*\S)?$}` + The value to set for this parameter. #### Parameters The following parameters are available in the `postgresql_conf` type. +* [`key`](#-postgresql_conf--key) * [`name`](#-postgresql_conf--name) * [`provider`](#-postgresql_conf--provider) +* [`target`](#-postgresql_conf--target) + +##### `key` + +Valid values: `%r{^[\w.]+$}` + +The Postgresql parameter to manage. ##### `name` @@ -4077,13 +4351,19 @@ Valid values: `%r{^[\w.]+$}` namevar -The postgresql parameter name to manage. +A unique title for the resource. ##### `provider` The specific backend to use for this `postgresql_conf` resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform. +##### `target` + +Valid values: `%r{^/\S+[a-z0-9(/)-]*\w+.conf$}` + +The path to the postgresql config file + ### `postgresql_conn_validator` Verify that a connection can be successfully established between a node diff --git a/lib/puppet/provider/postgresql_conf/parsed.rb b/lib/puppet/provider/postgresql_conf/parsed.rb deleted file mode 100644 index 8918769cab..0000000000 --- a/lib/puppet/provider/postgresql_conf/parsed.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'puppet/provider/parsedfile' - -Puppet::Type.type(:postgresql_conf).provide( - :parsed, - parent: Puppet::Provider::ParsedFile, - default_target: '/etc/postgresql.conf', - filetype: :flat, -) do - desc 'Set key/values in postgresql.conf.' - - text_line :comment, match: %r{^\s*#} - text_line :blank, match: %r{^\s*$} - - record_line :parsed, - fields: ['name', 'value', 'comment'], - optional: ['comment'], - match: %r{^\s*([\w.]+)\s*=?\s*(.*?)(?:\s*#\s*(.*))?\s*$}, - to_line: proc { |h| - # simple string and numeric values don't need to be enclosed in quotes - val = if h[:value].is_a?(Numeric) - h[:value].to_s - elsif h[:value].is_a?(Array) - # multiple listen_addresses specified as a string containing a comma-speparated list - h[:value].join(', ') - else - h[:value] - end - dontneedquote = val.match(%r{^(\d+.?\d+|\w+)$}) - dontneedequal = h[:name].match(%r{^(include|include_if_exists)$}i) - - str = h[:name].downcase # normalize case - str += dontneedequal ? ' ' : ' = ' - str += "'" unless dontneedquote && !dontneedequal - str += val - str += "'" unless dontneedquote && !dontneedequal - str += " # #{h[:comment]}" unless h[:comment].nil? || h[:comment] == :absent - str - }, - post_parse: proc { |h| - h[:name].downcase! # normalize case - h[:value].gsub!(%r{(^'|'$)}, '') # strip out quotes - } -end diff --git a/lib/puppet/provider/postgresql_conf/ruby.rb b/lib/puppet/provider/postgresql_conf/ruby.rb new file mode 100644 index 0000000000..63b87478d1 --- /dev/null +++ b/lib/puppet/provider/postgresql_conf/ruby.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +# This provider is used to manage postgresql.conf files +# It uses ruby to parse the config file and +# to add, remove or modify settings. +# +# The provider is able to parse postgresql.conf files with the following format: +# key = value # comment + +Puppet::Type.type(:postgresql_conf).provide(:ruby) do + desc 'Set keys, values and comments in a postgresql config file.' + confine kernel: 'Linux' + + # The function pareses the postgresql.conf and figures out which active settings exist in a config file and returns an array of hashes + # + def parse_config + # open the config file + file = File.open(resource[:target]) + # regex to match active keys, values and comments + active_values_regex = %r{^\s*(?[\w.]+)\s*=?\s*(?.*?)(?:\s*#\s*(?.*))?\s*$} + # empty array to be filled with hashes + active_settings = [] + # iterate the file and construct a hash for every matching/active setting + # the hash is pushed to the array and the array is returned + File.foreach(file).with_index do |line, index| + line_number = index + 1 + matches = line.match(active_values_regex) + if matches + value = if matches[:value].to_i.to_s == matches[:value] + matches[:value].to_i + elsif matches[:value].to_f.to_s == matches[:value] + matches[:value].to_f + else + matches[:value].delete("'") + end + attributes_hash = { line_number: line_number, key: matches[:key], ensure: 'present', value: value, comment: matches[:comment] } + active_settings.push(attributes_hash) + end + end + Puppet.debug("DEBUG: parse_config Active Settings found in Postgreql config file: #{active_settings}") + active_settings + end + + # Deletes an existing header from a parsed postgresql.conf configuration file + # + # @param [Array] lines of the parsed postgresql configuration file + def delete_header(lines) + header_regex = %r{^# HEADER:.*} + lines.delete_if do |entry| + entry.match?(header_regex) + end + end + + # Adds a header to a parsed postgresql.conf configuration file, after all other changes are made + # + # @param [Array] lines of the parsed postgresql configuration file + def add_header(lines) + timestamp = Time.now.strftime('%F %T %z') + header = ["# HEADER: This file was autogenerated at #{timestamp}\n", + "# HEADER: by puppet. While it can still be managed manually, it\n", + "# HEADER: is definitely not recommended.\n"] + header + lines + end + + # This function writes the config file, it removes the old header, adds a new one and writes the file + # + # @param [File] the file object of the postgresql configuration file + # @param [Array] lines of the parsed postgresql configuration file + def write_config(file, lines) + lines = delete_header(lines) + lines = add_header(lines) + File.write(file, lines.join) + end + + # check, if resource exists in postgresql.conf file + def exists? + select = parse_config.select { |hash| hash[:key] == resource[:key] } + raise ParserError, "found multiple config items of #{resource[:key]} found, please fix this" if select.length > 1 + return false if select.empty? + + @result = select.first + Puppet.debug("DEBUG: exists? @result: #{@result}") + true + end + + # remove resource if exists and is set to absent + def destroy + entry_regex = %r{#{resource[:key]}.*=.*#{resource[:value]}} + file = File.open(resource[:target]) + lines = File.readlines(file) + + lines.delete_if do |entry| + entry.match?(entry_regex) + end + write_config(file, lines) + end + + # create resource if it does not exists + def create + file = File.open(resource[:target]) + lines = File.readlines(file) + new_line = line(key: resource[:key], value: resource[:value], comment: resource[:comment]) + + lines.push(new_line) + write_config(file, lines) + end + + # getter - get value of a resource + def value + @result[:value] + end + + # getter - get comment of a resource + def comment + @result[:comment] + end + + # setter - set value of a resource + def value=(_value) + file = File.open(resource[:target]) + lines = File.readlines(file) + active_values_regex = %r{^\s*(?[\w.]+)\s*=?\s*(?.*?)(?:\s*#\s*(?.*))?\s*$} + new_line = line(key: resource[:key], value: resource[:value], comment: resource[:comment]) + + lines.each_with_index do |line, index| + matches = line.to_s.match(active_values_regex) + lines[index] = new_line if matches && (matches[:key] == resource[:key] && matches[:value] != resource[:value]) + end + write_config(file, lines) + end + + # setter - set comment of a resource + def comment=(_comment) + file = File.open(resource[:target]) + lines = File.readlines(file) + active_values_regex = %r{^\s*(?[\w.]+)\s*=?\s*(?.*?)(?:\s*#\s*(?.*))?\s*$} + new_line = line(key: resource[:key], value: resource[:value], comment: resource[:comment]) + + lines.each_with_index do |line, index| + matches = line.to_s.match(active_values_regex) + lines[index] = new_line if matches && (matches[:key] == resource[:key] && matches[:comment] != resource[:comment]) + end + write_config(file, lines) + end + + private + + # Takes elements for a postgresql.conf configuration line and formats them properly + # + # @param [String] key postgresql.conf configuration option + # @param [String] value the value for the configuration option + # @param [String] comment optional comment that will be added at the end of the line + # @return [String] line the whole line for the config file, with \n + def line(key: '', value: '', comment: nil) + value = value.to_s if value.is_a?(Numeric) + dontneedquote = value.match(%r{^(\d+.?\d+|\w+)$}) + dontneedequal = key.match(%r{^(include|include_if_exists)$}i) + line = key.downcase # normalize case + line += dontneedequal ? ' ' : ' = ' + line += "'" unless dontneedquote && !dontneedequal + line += value + line += "'" unless dontneedquote && !dontneedequal + line += " # #{comment}" unless comment.nil? || comment == :absent + line += "\n" + line + end +end diff --git a/lib/puppet/type/postgresql_conf.rb b/lib/puppet/type/postgresql_conf.rb index c014ac0fe8..432f5aa877 100644 --- a/lib/puppet/type/postgresql_conf.rb +++ b/lib/puppet/type/postgresql_conf.rb @@ -2,28 +2,40 @@ Puppet::Type.newtype(:postgresql_conf) do @doc = 'This type allows puppet to manage postgresql.conf parameters.' - ensurable newparam(:name) do - desc 'The postgresql parameter name to manage.' - isnamevar + desc 'A unique title for the resource.' + newvalues(%r{^[\w.]+$}) + end + newparam(:key) do + desc 'The Postgresql parameter to manage.' newvalues(%r{^[\w.]+$}) end newproperty(:value) do desc 'The value to set for this parameter.' - end + newvalues(%r{^\S(.*\S)?$}) - newproperty(:target) do - desc 'The path to postgresql.conf' - defaultto do - if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) - @resource.class.defaultprovider.default_target + munge do |value| + if value.to_i.to_s == value + value.to_i + elsif value.to_f.to_s == value + value.to_f else - nil + value end end end + + newproperty(:comment) do + desc 'The comment to set for this parameter.' + newvalues(%r{^[\w\W]+$}) + end + + newparam(:target) do + desc 'The path to the postgresql config file' + newvalues(%r{^/\S+[a-z0-9(/)-]*\w+.conf$}) + end end diff --git a/manifests/server.pp b/manifests/server.pp index 89d45fb7d5..2516e43611 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -167,7 +167,7 @@ Optional[String[1]] $locale = $postgresql::params::locale, Optional[String[1]] $lc_messages = undef, Optional[Boolean] $data_checksums = $postgresql::params::data_checksums, - Optional[String[1]] $username = undef, + Optional[String[1]] $username = $user, Optional[String[1]] $timezone = $postgresql::params::timezone, Boolean $manage_pg_hba_conf = $postgresql::params::manage_pg_hba_conf, diff --git a/manifests/server/config_entry.pp b/manifests/server/config_entry.pp index 65cd68315c..d17b844a18 100644 --- a/manifests/server/config_entry.pp +++ b/manifests/server/config_entry.pp @@ -4,12 +4,14 @@ # @param key Defines the key/name for the setting. Defaults to $name # @param value Defines the value for the setting. # @param path Path for postgresql.conf +# @param comment Defines the comment for the setting. The # is added by default. # define postgresql::server::config_entry ( - Enum['present', 'absent'] $ensure = 'present', - String[1] $key = $name, - Optional[Variant[String[1], Numeric, Array[String[1]]]] $value = undef, - Stdlib::Absolutepath $path = $postgresql::server::postgresql_conf_path + Enum['present', 'absent'] $ensure = 'present', + String[1] $key = $name, + Optional[Variant[String[1], Numeric, Array[String[1]]]] $value = undef, + Stdlib::Absolutepath $path = $postgresql::server::postgresql_conf_path, + Optional[String[1]] $comment = undef, ) { # Those are the variables that are marked as "(change requires restart)" # on postgresql.conf. Items are ordered as on postgresql.conf. @@ -85,8 +87,9 @@ postgresql_conf { $name: ensure => $ensure, target => $path, - name => $key, + key => $key, value => $value, + comment => $comment, require => Class['postgresql::server::initdb'], } } diff --git a/manifests/server/database.pp b/manifests/server/database.pp index c48e01f805..7d95e76056 100644 --- a/manifests/server/database.pp +++ b/manifests/server/database.pp @@ -8,6 +8,7 @@ # @param encoding Overrides the character set during creation of the database. # @param locale Overrides the locale during creation of the database. # @param istemplate Defines the database as a template if set to true. +# @param instance The name of the Postgresql database instance. # @param connect_settings Specifies a hash of environment variables used when connecting to a remote server. # @param psql_path Specifies the path to the psql command. # @param default_db Specifies the name of the default database to connect with. On most systems this is 'postgres'. @@ -23,6 +24,7 @@ Optional[String[1]] $encoding = $postgresql::server::encoding, Optional[String[1]] $locale = $postgresql::server::locale, Boolean $istemplate = false, + String[1] $instance = 'main', Hash $connect_settings = $postgresql::server::default_connect_settings, String[1] $user = $postgresql::server::user, String[1] $group = $postgresql::server::group, @@ -41,6 +43,7 @@ psql_path => $psql_path, port => $port_override, connect_settings => $connect_settings, + instance => $instance, } # Optionally set the locale switch. Older versions of createdb may not accept diff --git a/manifests/server/db.pp b/manifests/server/db.pp index 01cc61b665..513e548ed7 100644 --- a/manifests/server/db.pp +++ b/manifests/server/db.pp @@ -14,6 +14,7 @@ # @param port Specifies the port where the PostgreSQL server is listening on. # @param psql_user Overrides the default PostgreSQL super user and owner of PostgreSQL related files in the file system. # @param psql_group Overrides the default PostgreSQL user group to be used for related files in the file system. +# @param instance The name of the Postgresql database instance. define postgresql::server::db ( String[1] $user, Optional[Variant[String, Sensitive[String]]] $password = undef, @@ -29,6 +30,7 @@ Optional[Stdlib::Port] $port = undef, String[1] $psql_user = $postgresql::server::user, String[1] $psql_group = $postgresql::server::group, + String[1] $instance = 'main', ) { if ! defined(Postgresql::Server::Database[$dbname]) { postgresql::server::database { $dbname: diff --git a/manifests/server/default_privileges.pp b/manifests/server/default_privileges.pp index 771c86cd58..815abaebbd 100644 --- a/manifests/server/default_privileges.pp +++ b/manifests/server/default_privileges.pp @@ -12,6 +12,7 @@ # @param psql_path Specifies the OS user for running psql. Default value: The default user for the module, usually 'postgres'. # @param port Specifies the port to access the server. Default value: The default user for the module, usually '5432'. # @param connect_settings Specifies a hash of environment variables used when connecting to a remote server. +# @param instance The name of the Postgresql database instance. # @param group Specifies the user group to which the privileges will be granted. define postgresql::server::default_privileges ( String $role, @@ -34,6 +35,7 @@ String $group = $postgresql::server::group, Stdlib::Absolutepath $psql_path = $postgresql::server::psql_path, Optional[String] $target_role = undef, + String[1] $instance = 'main', ) { $version = pick($connect_settings['DBVERSION'],postgresql::default('version')) $port_override = pick($connect_settings['PGPORT'], $port) @@ -160,6 +162,7 @@ psql_path => $psql_path, unless => $unless_cmd, environment => 'PGOPTIONS=--client-min-messages=error', + instance => $instance, } if defined(Postgresql::Server::Role[$role]) { diff --git a/manifests/server/extension.pp b/manifests/server/extension.pp index 79dfd3b126..196c077c0b 100644 --- a/manifests/server/extension.pp +++ b/manifests/server/extension.pp @@ -21,6 +21,7 @@ # @param port Port to use when connecting. # @param connect_settings Specifies a hash of environment variables used when connecting to a remote server. # @param database_resource_name Specifies the resource name of the DB being managed. Defaults to the parameter $database, if left blank. +# @param instance The name of the Postgresql database instance. # @param psql_path Specifies the path to the psql command. # @param user Overrides the default PostgreSQL super user and owner of PostgreSQL related files in the file system. # @param group Overrides the default postgres user group to be used for related files in the file system. @@ -35,6 +36,7 @@ Stdlib::Port $port = postgresql::default('port'), Hash $connect_settings = postgresql::default('default_connect_settings'), String[1] $database_resource_name = $database, + String[1] $instance = 'main', String[1] $user = postgresql::default('user'), String[1] $group = postgresql::default('group'), Stdlib::Absolutepath $psql_path = postgresql::default('psql_path'), @@ -86,6 +88,7 @@ db => $database, port => $port_override, command => $command, + instance => $instance, unless => "SELECT 1 WHERE ${unless_mod}EXISTS (SELECT 1 FROM pg_extension WHERE extname = '${extension}')", require => $psql_cmd_require, before => $psql_cmd_before, @@ -113,6 +116,7 @@ connect_settings => $connect_settings, db => $database, port => $port_override, + instance => $instance, require => Postgresql_psql["${database}: ${command}"], } @@ -146,6 +150,7 @@ psql_path => $psql_path, connect_settings => $connect_settings, command => $alter_extension_sql, + instance => $instance, unless => $update_unless, } } diff --git a/manifests/server/grant.pp b/manifests/server/grant.pp index 68591851bb..e9c0894003 100644 --- a/manifests/server/grant.pp +++ b/manifests/server/grant.pp @@ -18,6 +18,7 @@ # @param ensure Specifies whether to grant or revoke the privilege. Default is to grant the privilege. Valid values: 'present', 'absent'. # @param group Sets the OS group to run psql # @param psql_path Sets the path to psql command +# @param instance The name of the Postgresql database instance. define postgresql::server::grant ( String $role, String $db, @@ -47,6 +48,7 @@ Enum['present', 'absent'] $ensure = 'present', String $group = $postgresql::server::group, Stdlib::Absolutepath $psql_path = $postgresql::server::psql_path, + String[1] $instance = 'main', ) { case $ensure { default: { @@ -470,6 +472,7 @@ psql_user => $psql_user, psql_group => $group, psql_path => $psql_path, + instance => $instance, unless => $_unless, onlyif => $_onlyif, } diff --git a/manifests/server/grant_role.pp b/manifests/server/grant_role.pp index 4edf59ea0d..11ef6a70de 100644 --- a/manifests/server/grant_role.pp +++ b/manifests/server/grant_role.pp @@ -7,10 +7,12 @@ # @param psql_user Sets the OS user to run psql. # @param port Port to use when connecting. # @param connect_settings Specifies a hash of environment variables used when connecting to a remote server. +# @param instance The name of the Postgresql database instance. define postgresql::server::grant_role ( String[1] $group, String[1] $role = $name, Enum['present', 'absent'] $ensure = 'present', + String[1] $instance = 'main', String[1] $psql_db = $postgresql::server::default_database, String[1] $psql_user = $postgresql::server::user, Stdlib::Port $port = $postgresql::server::port, @@ -36,6 +38,7 @@ db => $psql_db, psql_user => $psql_user, port => $port, + instance => $instance, connect_settings => $connect_settings, } diff --git a/manifests/server/instance/config.pp b/manifests/server/instance/config.pp index 630bcff3e4..d596d0846b 100644 --- a/manifests/server/instance/config.pp +++ b/manifests/server/instance/config.pp @@ -94,7 +94,8 @@ if $pg_hba_conf_defaults { Postgresql::Server::Pg_hba_rule { database => 'all', - user => 'all', + target => $pg_hba_conf_path, + user => 'all', } postgresql::server::pg_hba_rule { @@ -135,6 +136,10 @@ auth_method => $_pg_hba_auth_password_encryption, order => 101; } + } else { + Postgresql::Server::Pg_hba_rule { + target => $pg_hba_conf_path, + } } # $ipv4acls and $ipv6acls are arrays of rule strings @@ -148,6 +153,11 @@ } } } + # set default postgresql_conf_path here so the path is configurable in instances for + # default values like port or listen_address + Postgresql::Server::Config_entry { + path => $postgresql_conf_path, + } if $manage_postgresql_conf_perms { file { $postgresql_conf_path: @@ -242,12 +252,11 @@ notify => Class['postgresql::server::reload'], } } - # RHEL based systems and Gentoo need variables set for $PGPORT, $DATA_DIR or $PGDATA via a drop-in file - if $facts['os']['family'] == 'RedHat' or ($facts['os']['family'] == 'Gentoo' and $facts['service_provider'] == 'systemd') { - postgresql::server::instance::systemd { $service_name: - port => $port, - datadir => $datadir, - extra_systemd_config => $extra_systemd_config, - } + + postgresql::server::instance::systemd { $name: + port => $port, + datadir => $datadir, + extra_systemd_config => $extra_systemd_config, + service_name => $service_name, } } diff --git a/manifests/server/instance/late_initdb.pp b/manifests/server/instance/late_initdb.pp index 05a93f81a6..bc011b9a01 100644 --- a/manifests/server/instance/late_initdb.pp +++ b/manifests/server/instance/late_initdb.pp @@ -25,6 +25,7 @@ psql_group => $group, psql_path => $psql_path, port => $port, + instance => $name, cwd => $module_workdir, } diff --git a/manifests/server/instance/systemd.pp b/manifests/server/instance/systemd.pp index d3f217e09e..b9a46a2336 100644 --- a/manifests/server/instance/systemd.pp +++ b/manifests/server/instance/systemd.pp @@ -5,26 +5,36 @@ define postgresql::server::instance::systemd ( Stdlib::Port $port, Stdlib::Absolutepath $datadir, + String[1] $instance_name = $name, Optional[String[1]] $extra_systemd_config = undef, String[1] $service_name = $name, Enum[present, absent] $drop_in_ensure = 'present', ) { - # Template uses: - # - $port - # - $datadir - # - $extra_systemd_config - systemd::dropin_file { "${service_name}.conf": - ensure => $drop_in_ensure, - unit => "${service_name}.service", - owner => 'root', - group => 'root', - content => epp('postgresql/systemd-override.conf.epp', { - port => $port, - datadir => $datadir, - extra_systemd_config => $extra_systemd_config, + if $facts['service_provider'] == 'systemd' { + if $facts['os']['family'] in ['RedHat', 'Gentoo'] { + # RHEL 7 and 8 both support drop-in files for systemd units. + # Gentoo also supports drop-in files. + # RHEL based Systems need Variables set for $PGPORT, $DATA_DIR or $PGDATA, thats what the drop-in file is for. + # For additional instances (!= 'main') we need a new systemd service anyhow and use one systemd-file. no dropin needed. + # + # Template uses: + # - $port + # - $datadir + # - $extra_systemd_config + systemd::dropin_file { "${service_name}.conf": + ensure => $drop_in_ensure, + unit => "${service_name}.service", + owner => 'root', + group => 'root', + content => epp('postgresql/systemd-override.conf.epp', { + port => $port, + datadir => $datadir, + extra_systemd_config => $extra_systemd_config, + } + ), + notify => Class['postgresql::server::service'], + before => Class['postgresql::server::reload'], } - ), - notify => Class['postgresql::server::service'], - before => Class['postgresql::server::reload'], + } } } diff --git a/manifests/server/pg_hba_rule.pp b/manifests/server/pg_hba_rule.pp index 1144327951..e7945669e0 100644 --- a/manifests/server/pg_hba_rule.pp +++ b/manifests/server/pg_hba_rule.pp @@ -32,9 +32,6 @@ String[1] $description = 'none', Optional[String] $auth_option = undef, Variant[String, Integer] $order = 150, - - # Needed for testing primarily, support for multiple files is not really - # working. Stdlib::Absolutepath $target = $postgresql::server::pg_hba_conf_path, String $postgresql_version = $postgresql::server::_version ) { diff --git a/manifests/server/reassign_owned_by.pp b/manifests/server/reassign_owned_by.pp index 30d8729318..ce98f2601b 100644 --- a/manifests/server/reassign_owned_by.pp +++ b/manifests/server/reassign_owned_by.pp @@ -10,6 +10,7 @@ # @param connect_settings Specifies a hash of environment variables used when connecting to a remote server. # @param group Sets the OS group to run psql # @param psql_path Sets the path to psql command +# @param instance The name of the Postgresql database instance. define postgresql::server::reassign_owned_by ( String $old_role, String $new_role, @@ -19,6 +20,7 @@ Hash $connect_settings = $postgresql::server::default_connect_settings, String[1] $group = $postgresql::server::group, Stdlib::Absolutepath $psql_path = $postgresql::server::psql_path, + String[1] $instance = 'main', ) { $sql_command = "REASSIGN OWNED BY \"${old_role}\" TO \"${new_role}\"" @@ -43,6 +45,7 @@ psql_user => $psql_user, psql_group => $group, psql_path => $psql_path, + instance => $instance, onlyif => $onlyif, } diff --git a/manifests/server/role.pp b/manifests/server/role.pp index 91d07e7798..1b37ce282c 100644 --- a/manifests/server/role.pp +++ b/manifests/server/role.pp @@ -23,6 +23,7 @@ # May need to specify if '/tmp' is on volume mounted with noexec option. # @param hash Specify the hash method for pg password # @param salt Specify the salt use for the scram-sha-256 encoding password (default username) +# @param instance The name of the Postgresql database instance. define postgresql::server::role ( Boolean $update_password = true, Variant[Boolean, String, Sensitive[String]] $password_hash = false, @@ -44,6 +45,7 @@ Enum['present', 'absent'] $ensure = 'present', Optional[Enum['md5', 'scram-sha-256']] $hash = undef, Optional[Variant[String[1], Integer]] $salt = undef, + String[1] $instance = 'main', ) { $password_hash_unsensitive = if $password_hash =~ Sensitive[String] { $password_hash.unwrap @@ -54,14 +56,15 @@ $version = pick($connect_settings['DBVERSION'], postgresql::default('version')) Postgresql_psql { - db => $db, - port => $port_override, - psql_user => $psql_user, - psql_group => $psql_group, - psql_path => $psql_path, + db => $db, + port => $port_override, + psql_user => $psql_user, + psql_group => $psql_group, + psql_path => $psql_path, connect_settings => $connect_settings, - cwd => $module_workdir, - require => Postgresql_psql["CREATE ROLE ${username} ENCRYPTED PASSWORD ****"], + instance => $instance, + cwd => $module_workdir, + require => Postgresql_psql["CREATE ROLE ${username} ENCRYPTED PASSWORD ****"], } if $ensure == 'present' { diff --git a/manifests/server/schema.pp b/manifests/server/schema.pp index 369f638c35..e8405792f7 100644 --- a/manifests/server/schema.pp +++ b/manifests/server/schema.pp @@ -15,6 +15,7 @@ # @param module_workdir # Specifies working directory under which the psql command should be executed. # May need to specify if '/tmp' is on volume mounted with noexec option. +# @param instance The name of the Postgresql database instance. # @example # postgresql::server::schema {'private': # db => 'template1', @@ -29,6 +30,7 @@ String[1] $group = $postgresql::server::group, Stdlib::Absolutepath $psql_path = $postgresql::server::psql_path, Stdlib::Absolutepath $module_workdir = $postgresql::server::module_workdir, + String[1] $instance = 'main', ) { Postgresql::Server::Db <| dbname == $db |> -> Postgresql::Server::Schema[$name] @@ -36,13 +38,14 @@ $port_override = pick($connect_settings['PGPORT'], $port) Postgresql_psql { - db => $db, - psql_user => $user, - psql_group => $group, - psql_path => $psql_path, - port => $port_override, - cwd => $module_workdir, + db => $db, + psql_user => $user, + psql_group => $group, + psql_path => $psql_path, + port => $port_override, + cwd => $module_workdir, connect_settings => $connect_settings, + instance => $instance, } postgresql_psql { "${db}: CREATE SCHEMA \"${schema}\"": diff --git a/manifests/server/tablespace.pp b/manifests/server/tablespace.pp index 9f166cc7d2..2774732780 100644 --- a/manifests/server/tablespace.pp +++ b/manifests/server/tablespace.pp @@ -12,6 +12,7 @@ # @param module_workdir # Specifies working directory under which the psql command should be executed. # May need to specify if '/tmp' is on volume mounted with noexec option. +# @param instance The name of the Postgresql database instance. define postgresql::server::tablespace ( String[1] $location, Boolean $manage_location = true, @@ -23,6 +24,7 @@ String[1] $group = $postgresql::server::group, Stdlib::Absolutepath $psql_path = $postgresql::server::psql_path, String[1] $module_workdir = $postgresql::server::module_workdir, + String[1] $instance = 'main', ) { # If the connection settings do not contain a port, then use the local server port $port_override = pick($connect_settings['PGPORT'], $port) @@ -34,6 +36,7 @@ port => $port_override, connect_settings => $connect_settings, cwd => $module_workdir, + instance => $instance, } if($manage_location) { diff --git a/manifests/server_instance.pp b/manifests/server_instance.pp new file mode 100644 index 0000000000..49d2bbb8f7 --- /dev/null +++ b/manifests/server_instance.pp @@ -0,0 +1,132 @@ +# @summary define to install and manage additional postgresql instances +# @param instance_name The name of the instance. +# @param instance_user The user to run the instance as. +# @param instance_group The group to run the instance as. +# @param instance_user_homedirectory The home directory of the instance user. +# @param manage_instance_user_and_group Should Puppet manage the instance user and it's primary group?. +# @param instance_directories directories needed for the instance. Option to manage the directory properties for each directory. +# @param initdb_settings Specifies a hash witn parameters for postgresql::server::instance::initdb +# @param config_settings Specifies a hash with parameters for postgresql::server::instance::config +# @param service_settings Specifies a hash with parameters for postgresql::server:::instance::service +# @param passwd_settings Specifies a hash with parameters for postgresql::server::instance::passwd +# @param roles Specifies a hash from which to generate postgresql::server::role resources. +# @param config_entries Specifies a hash from which to generate postgresql::server::config_entry resources. +# @param pg_hba_rules Specifies a hash from which to generate postgresql::server::pg_hba_rule resources. +# @param databases Specifies a hash from which to generate postgresql::server::database resources. +# @param databases_and_users Specifies a hash from which to generate postgresql::server::db resources. +# @param database_grants Specifies a hash from which to generate postgresql::server::database_grant resources. +# @param table_grants Specifies a hash from which to generate postgresql::server::table_grant resources. +define postgresql::server_instance ( + String[1] $instance_name = $name, + Boolean $manage_instance_user_and_group = true, + Hash $instance_directories = {}, + String[1] $instance_user = $instance_name, + String[1] $instance_group = $instance_name, + Stdlib::Absolutepath $instance_user_homedirectory = "/opt/pgsql/data/home/${instance_user}", + Hash $initdb_settings = {}, + Hash $config_settings = {}, + Hash $service_settings = {}, + Hash $passwd_settings = {}, + Hash $roles = {}, + Hash $config_entries = {}, + Hash $pg_hba_rules = {}, + Hash $databases_and_users = {}, + Hash $databases = {}, + Hash $database_grants = {}, + Hash $table_grants = {}, +) { + unless($facts['os']['family'] == 'RedHat' and $facts['os']['release']['major'] == '8') { + warning('This define postgresql::server_instance is only tested on RHEL8') + } + $instance_directories.each |Stdlib::Absolutepath $directory, Hash $directory_settings| { + file { $directory: + * => $directory_settings, + } + } + + if $manage_instance_user_and_group { + user { $instance_user: + managehome => true, + system => true, + home => $instance_user_homedirectory, + gid => $instance_group, + } + group { $instance_group: + system => true, + } + } + postgresql::server::instance::initdb { $instance_name: + * => $initdb_settings, + } + postgresql::server::instance::config { $instance_name: + * => $config_settings, + } + postgresql::server::instance::service { $instance_name: + * => $service_settings, + port => $config_settings['port'], + user => $instance_user, + } + postgresql::server::instance::passwd { $instance_name: + * => $passwd_settings, + } + + $roles.each |$rolename, $role| { + postgresql::server::role { $rolename: + * => $role, + psql_user => $instance_user, + psql_group => $instance_group, + port => $config_settings['port'], + instance => $instance_name, + } + } + + $config_entries.each |$entry, $settings| { + $value = $settings['value'] + $comment = $settings['comment'] + postgresql::server::config_entry { "${entry}_${$instance_name}": + ensure => bool2str($value =~ Undef, 'absent', 'present'), + key => $entry, + value => $value, + comment => $comment, + path => $config_settings['postgresql_conf_path'], + } + } + $pg_hba_rules.each |String[1] $rule_name, Postgresql::Pg_hba_rule $rule| { + $rule_title = "${rule_name} for instance ${name}" + postgresql::server::pg_hba_rule { $rule_title: + * => $rule, + target => $config_settings['pg_hba_conf_path'], # TODO: breaks if removed + } + } + $databases_and_users.each |$database, $database_details| { + postgresql::server::db { $database: + * => $database_details, + psql_user => $instance_user, + psql_group => $instance_group, + port => $config_settings['port'], + } + } + $databases.each |$database, $database_details| { + postgresql::server::database { $database: + * => $database_details, + user => $instance_user, + group => $instance_group, + port => $config_settings['port'], + } + } + $database_grants.each |$db_grant_title, $dbgrants| { + postgresql::server::database_grant { $db_grant_title: + * => $dbgrants, + psql_user => $instance_user, + psql_group => $instance_group, + port => $config_settings['port'], + } + } + $table_grants.each |$table_grant_title, $tgrants| { + postgresql::server::table_grant { $table_grant_title: + * => $tgrants, + psql_user => $instance_user, + port => $config_settings['port'], + } + } +} diff --git a/spec/acceptance/server_instance_spec.rb b/spec/acceptance/server_instance_spec.rb new file mode 100644 index 0000000000..93192bafb3 --- /dev/null +++ b/spec/acceptance/server_instance_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +# run a test task +require 'spec_helper_acceptance' + +describe 'postgresql instance test1', if: os[:family] == 'redhat' && os[:release].start_with?('8') do + pp = <<-MANIFEST + # set global defaults + class { 'postgresql::globals': + encoding => 'UTF-8', + locale => 'en_US.UTF-8', + manage_package_repo => false, + manage_dnf_module => true, + needs_initdb => true, + version => '13', + } + # stop default main instance + class { 'postgresql::server': + service_ensure => 'stopped', + service_enable => false, + } + # define instance test1 + postgresql::server_instance { 'test1': + instance_user => 'ins_test1', + instance_group => 'ins_test1', + instance_directories => { + '/opt/pgsql' => { 'ensure' => 'directory' }, + '/opt/pgsql/backup' => { 'ensure' => 'directory' }, + '/opt/pgsql/data' => { 'ensure' => 'directory' }, + '/opt/pgsql/data/13' => { 'ensure' => 'directory' }, + '/opt/pgsql/data/home' => { 'ensure' => 'directory' }, + '/opt/pgsql/wal' => { 'ensure' => 'directory' }, + '/opt/pgsql/log' => { 'ensure' => 'directory' }, + '/opt/pgsql/log/13' => { 'ensure' => 'directory' }, + '/opt/pgsql/log/13/test1' => { 'ensure' => 'directory' }, + }, + config_settings => { + 'pg_hba_conf_path' => '/opt/pgsql/data/13/test1/pg_hba.conf', + 'postgresql_conf_path' => '/opt/pgsql/data/13/test1/postgresql.conf', + 'pg_ident_conf_path' => '/opt/pgsql/data/13/test1/pg_ident.conf', + 'datadir' => '/opt/pgsql/data/13/test1', + 'service_name' => 'postgresql@13-test1', + 'port' => 5433, + 'pg_hba_conf_defaults' => false, + 'manage_selinux' => true, + }, + service_settings => { + 'service_name' => 'postgresql@13-test1', + 'service_status' => 'systemctl status postgresql@13-test1.service', + 'service_ensure' => 'running', + 'service_enable' => true, + }, + initdb_settings => { + 'auth_local' => 'peer', + 'auth_host' => 'md5', + 'needs_initdb' => true, + 'datadir' => '/opt/pgsql/data/13/test1', + 'encoding' => 'UTF-8', + 'lc_messages' => 'en_US.UTF8', + 'locale' => 'en_US.UTF8', + 'data_checksums' => false, + 'group' => 'postgres', + 'user' => 'postgres', + 'username' => 'ins_test1', + }, + config_entries => { + 'authentication_timeout' => { + 'value' => '1min', + 'comment' => 'a test', + }, + 'log_statement_stats' => { 'value' => 'off' }, + 'autovacuum_vacuum_scale_factor' => { 'value' => 0.3 }, + }, + databases => { + 'testdb1' => { + 'encoding' => 'UTF8', + 'locale' => 'en_US.UTF8', + 'owner' => 'dba_test1', + }, + 'testdb2' => { + 'encoding' => 'UTF8', + 'locale' => 'en_US.UTF8', + 'owner' => 'dba_test1', + }, + }, + roles => { + 'ins_test1' => { + 'superuser' => true, + 'login' => true, + }, + 'dba_test1' => { + 'createdb' => true, + 'login' => true, + }, + 'app_test1' => { 'login' => true }, + 'rep_test1' => { + 'replication' => true, + 'login' => true, + }, + 'rou_test1' => { 'login' => true }, + }, + pg_hba_rules => { + 'local all INSTANCE user' => { + 'type' => 'local', + 'database' => 'all', + 'user' => 'ins_test1', + 'auth_method' => 'peer', + 'order' => 1, + }, + 'local all DB user' => { + 'type' => 'local', + 'database' => 'all', + 'user' => 'dba_test1', + 'auth_method' => 'peer', + 'order' => 2, + }, + 'local all APP user' => { + 'type' => 'local', + 'database' => 'all', + 'user' => 'app_test1', + 'auth_method' => 'peer', + 'order' => 3, + }, + 'local all READONLY user' => { + 'type' => 'local', + 'database' => 'all', + 'user' => 'rou_test1', + 'auth_method' => 'peer', + 'order' => 4, + }, + 'remote all INSTANCE user PGADMIN server' => { + 'type' => 'host', + 'database' => 'all', + 'user' => 'ins_test1', + 'address' => '192.168.22.131/32', + 'auth_method' => 'md5', + 'order' => 5, + }, + 'local replication INSTANCE user' => { + 'type' => 'local', + 'database' => 'replication', + 'user' => 'ins_test1', + 'auth_method' => 'peer', + 'order' => 6, + }, + 'local replication REPLICATION user' => { + 'type' => 'local', + 'database' => 'replication', + 'user' => 'rep_test1', + 'auth_method' => 'peer', + 'order' => 7, + }, + }, + } + MANIFEST + + it 'installs postgres instance test1' do + export_locales('en_US.UTF-8 ') + apply_manifest(pp, catch_failures: true) + end +end diff --git a/spec/defines/server_instance_spec.rb b/spec/defines/server_instance_spec.rb new file mode 100644 index 0000000000..89eadb06c8 --- /dev/null +++ b/spec/defines/server_instance_spec.rb @@ -0,0 +1,223 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'postgresql::server_instance' do + include_examples 'RedHat 8' + + let :pre_condition do + "class { 'postgresql::globals': + encoding => 'UTF-8', + locale => 'en_US.UTF-8', + manage_package_repo => false, + manage_dnf_module => true, + needs_initdb => true, + version => '13', + } + # stop default main instance + class { 'postgresql::server': + service_ensure => 'stopped', + service_enable => false, + }" + end + let(:title) { 'test1' } + let(:params) do + { + 'instance_user': 'ins_test1', + 'instance_group': 'ins_test1', + 'instance_directories': { '/opt/pgsql': { 'ensure' => 'directory' }, + '/opt/pgsql/backup': { 'ensure' => 'directory' }, + '/opt/pgsql/data': { 'ensure' => 'directory' }, + '/opt/pgsql/data/13': { 'ensure' => 'directory' }, + '/opt/pgsql/data/home': { 'ensure' => 'directory' }, + '/opt/pgsql/wal': { 'ensure' => 'directory' }, + '/opt/pgsql/log': { 'ensure' => 'directory' }, + '/opt/pgsql/log/13': { 'ensure' => 'directory' }, + '/opt/pgsql/log/13/test1': { 'ensure' => 'directory' }, }, + 'config_settings': { 'pg_hba_conf_path' => '/opt/pgsql/data/13/test1/pg_hba.conf', + 'postgresql_conf_path' => '/opt/pgsql/data/13/test1/postgresql.conf', + 'pg_ident_conf_path' => '/opt/pgsql/data/13/test1/pg_ident.conf', + 'datadir' => '/opt/pgsql/data/13/test1', + 'service_name' => 'postgresql@13-test1', + 'port' => 5433, + 'pg_hba_conf_defaults' => false }, + 'service_settings': { 'service_name' => 'postgresql@13-test1', + 'service_status' => 'systemctl status postgresql@13-test1.service', + 'service_ensure' => 'running', + 'service_enable' => true }, + 'initdb_settings': { 'auth_local' => 'peer', + 'auth_host' => 'md5', + 'needs_initdb' => true, + 'datadir' => '/opt/pgsql/data/13/test1', + 'encoding' => 'UTF-8', + 'lc_messages' => 'en_US.UTF8', + 'locale' => 'en_US.UTF8', + 'data_checksums' => false, + 'group' => 'postgres', + 'user' => 'postgres', + 'username' => 'ins_test1' }, + 'config_entries': { 'authentication_timeout': { 'value' => '1min', + 'comment' => 'a test' }, + 'log_statement_stats': { 'value' => 'off' }, + 'autovacuum_vacuum_scale_factor': { 'value' => 0.3 }, }, + 'databases': { 'testdb1': { 'encoding' => 'UTF8', + 'locale' => 'en_US.UTF8', + 'owner' => 'dba_test1' }, + 'testdb2': { 'encoding' => 'UTF8', + 'locale' => 'en_US.UTF8', + 'owner' => 'dba_test1' }, }, + 'roles': { 'ins_test1': { 'superuser' => true, + 'login' => true, }, + 'dba_test1': { 'createdb' => true, + 'login' => true, }, + 'app_test1': { 'login' => true }, + 'rep_test1': { 'replication' => true, + 'login' => true }, + 'rou_test1': { 'login' => true }, }, + 'pg_hba_rules': { 'local all INSTANCE user': { 'type' => 'local', + 'database' => 'all', + 'user' => 'ins_test1', + 'auth_method' => 'peer', + 'order' => 1 }, + 'local all DB user': { 'type' => 'local', + 'database' => 'all', + 'user' => 'dba_test1', + 'auth_method' => 'peer', + 'order' => 2 }, + 'local all APP user': { 'type' => 'local', + 'database' => 'all', + 'user' => 'app_test1', + 'auth_method' => 'peer', + 'order' => 3 }, + 'local all READONLY user': { 'type' => 'local', + 'database' => 'all', + 'user' => 'rou_test1', + 'auth_method' => 'peer', + 'order' => 4 }, + 'remote all INSTANCE user PGADMIN server': { 'type' => 'host', + 'database' => 'all', + 'user' => 'ins_test1', + 'address' => '192.168.22.131/32', + 'auth_method' => 'md5', + 'order' => 5 }, + 'local replication INSTANCE user': { 'type' => 'local', + 'database' => 'replication', + 'user' => 'ins_test1', + 'auth_method' => 'peer', + 'order' => 6 }, + 'local replication REPLICATION user': { 'type' => 'local', + 'database' => 'replication', + 'user' => 'rep_test1', + 'auth_method' => 'peer', + 'order' => 7 }, }, + } + end + + context 'with postgresql instance test1' do + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_postgresql__server_instance('test1') } + it { is_expected.to contain_user('ins_test1') } + it { is_expected.to contain_group('ins_test1') } + it { is_expected.to contain_service('postgresqld_instance_test1').with_name('postgresql@13-test1').with_status('systemctl status postgresql@13-test1.service') } + it { is_expected.to contain_systemd__dropin_file('postgresql@13-test1.conf') } + it { is_expected.to contain_postgresql_conn_validator('validate_service_is_running_instance_test1') } + it { is_expected.to contain_postgresql_conf('port_for_instance_test1') } + it { is_expected.to contain_postgresql_conf('log_statement_stats_test1') } + it { is_expected.to contain_postgresql_conf('data_directory_for_instance_test1') } + it { is_expected.to contain_postgresql_conf('autovacuum_vacuum_scale_factor_test1') } + it { is_expected.to contain_postgresql_conf('authentication_timeout_test1') } + it { is_expected.to contain_postgresql__server__role('app_test1') } + it { is_expected.to contain_postgresql__server__role('dba_test1') } + it { is_expected.to contain_postgresql__server__role('ins_test1') } + it { is_expected.to contain_postgresql__server__role('rep_test1') } + it { is_expected.to contain_postgresql__server__role('rou_test1') } + it { is_expected.to contain_anchor('postgresql::server::service::begin::test1') } + it { is_expected.to contain_anchor('postgresql::server::service::end::test1') } + it { is_expected.to contain_class('Postgresql::Dnfmodule') } + it { is_expected.to contain_class('Postgresql::Server::Install') } + it { is_expected.to contain_class('Postgresql::Server::Reload') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_local all APP user for instance test1') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_local all DB user for instance test1') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_local all INSTANCE user for instance test1') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_local all READONLY user for instance test1') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_local replication INSTANCE user for instance test1') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_local replication REPLICATION user for instance test1') } + it { is_expected.to contain_concat__fragment('pg_hba_rule_remote all INSTANCE user PGADMIN server for instance test1') } + it { is_expected.to contain_concat('/opt/pgsql/data/13/test1/pg_hba.conf') } + it { is_expected.to contain_concat('/opt/pgsql/data/13/test1/pg_ident.conf') } + it { is_expected.to contain_exec('postgresql_initdb_instance_test1') } + it { is_expected.to contain_file('/opt/pgsql/backup') } + it { is_expected.to contain_file('/opt/pgsql/data/13/test1/postgresql.conf') } + it { is_expected.to contain_file('/opt/pgsql/data/13/test1') } + it { is_expected.to contain_file('/opt/pgsql/data/13') } + it { is_expected.to contain_file('/opt/pgsql/data/home') } + it { is_expected.to contain_file('/opt/pgsql/data') } + it { is_expected.to contain_file('/opt/pgsql/log/13/test1') } + it { is_expected.to contain_file('/opt/pgsql/log/13') } + it { is_expected.to contain_file('/opt/pgsql/log') } + it { is_expected.to contain_file('/opt/pgsql/wal') } + it { is_expected.to contain_file('/opt/pgsql') } + it { is_expected.to contain_postgresql__server__config_entry('authentication_timeout_test1') } + it { is_expected.to contain_postgresql__server__config_entry('autovacuum_vacuum_scale_factor_test1') } + it { is_expected.to contain_postgresql__server__config_entry('data_directory_for_instance_test1') } + it { is_expected.to contain_postgresql__server__config_entry('log_statement_stats_test1') } + it { is_expected.to contain_postgresql__server__config_entry('password_encryption_for_instance_test1') } + it { is_expected.to contain_postgresql__server__config_entry('port_for_instance_test1') } + it { is_expected.to contain_postgresql__server__database('testdb1') } + it { is_expected.to contain_postgresql__server__database('testdb2') } + it { is_expected.to contain_postgresql__server__instance__config('test1') } + it { is_expected.to contain_postgresql__server__instance__initdb('test1') } + it { is_expected.to contain_postgresql__server__instance__passwd('test1') } + it { is_expected.to contain_postgresql__server__instance__service('test1') } + it { is_expected.to contain_postgresql__server__instance__systemd('test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('local all APP user for instance test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('local all DB user for instance test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('local all INSTANCE user for instance test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('local all READONLY user for instance test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('local replication INSTANCE user for instance test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('local replication REPLICATION user for instance test1') } + it { is_expected.to contain_postgresql__server__pg_hba_rule('remote all INSTANCE user PGADMIN server for instance test1') } + it { is_expected.to contain_postgresql_psql('ALTER DATABASE "testdb1" OWNER TO "dba_test1"') } + it { is_expected.to contain_postgresql_psql('ALTER DATABASE "testdb2" OWNER TO "dba_test1"') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" CONNECTION LIMIT -1') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" INHERIT') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" LOGIN') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" NOCREATEDB') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" NOCREATEROLE') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" NOREPLICATION') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "app_test1" NOSUPERUSER') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" CONNECTION LIMIT -1') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" CREATEDB') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" INHERIT') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" LOGIN') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" NOCREATEROLE') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" NOREPLICATION') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "dba_test1" NOSUPERUSER') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" CONNECTION LIMIT -1') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" INHERIT') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" LOGIN') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" NOCREATEDB') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" NOCREATEROLE') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" NOREPLICATION') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "ins_test1" SUPERUSER') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" CONNECTION LIMIT -1') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" INHERIT') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" LOGIN') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" NOCREATEDB') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" NOCREATEROLE') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" NOSUPERUSER') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rep_test1" REPLICATION') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" CONNECTION LIMIT -1') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" INHERIT') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" LOGIN') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" NOCREATEDB') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" NOCREATEROLE') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" NOREPLICATION') } + it { is_expected.to contain_postgresql_psql('ALTER ROLE "rou_test1" NOSUPERUSER') } + it { is_expected.to contain_postgresql_psql('CREATE ROLE app_test1 ENCRYPTED PASSWORD ****') } + it { is_expected.to contain_postgresql_psql('CREATE ROLE dba_test1 ENCRYPTED PASSWORD ****') } + it { is_expected.to contain_postgresql_psql('CREATE ROLE ins_test1 ENCRYPTED PASSWORD ****') } + it { is_expected.to contain_postgresql_psql('CREATE ROLE rep_test1 ENCRYPTED PASSWORD ****') } + it { is_expected.to contain_postgresql_psql('CREATE ROLE rou_test1 ENCRYPTED PASSWORD ****') } + end +end diff --git a/spec/unit/provider/postgresql_conf/parsed_spec.rb b/spec/unit/provider/postgresql_conf/parsed_spec.rb deleted file mode 100644 index 7f6fdaef05..0000000000 --- a/spec/unit/provider/postgresql_conf/parsed_spec.rb +++ /dev/null @@ -1,151 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'tempfile' - -provider_class = Puppet::Type.type(:postgresql_conf).provider(:parsed) - -describe provider_class do - let(:title) { 'postgresql_conf' } - let(:provider) do - conf_class = Puppet::Type.type(:postgresql_conf) - provider = conf_class.provider(:parsed) - conffile = tmpfilename('postgresql.conf') - allow_any_instance_of(provider).to receive(:target).and_return conffile # rubocop:disable RSpec/AnyInstance - provider - end - - after :each do - provider.initvars - end - - describe 'simple configuration that should be allowed' do - it 'parses a simple ini line' do - expect(provider.parse_line("listen_addreses = '*'")).to eq( - name: 'listen_addreses', value: '*', comment: nil, record_type: :parsed, - ) - end - - it 'parses a simple ini line (2)' do - expect(provider.parse_line(" listen_addreses = '*'")).to eq( - name: 'listen_addreses', value: '*', comment: nil, record_type: :parsed, - ) - end - - it 'parses a simple ini line (3)' do - expect(provider.parse_line("listen_addreses = '*' # dont mind me")).to eq( - name: 'listen_addreses', value: '*', comment: 'dont mind me', record_type: :parsed, - ) - end - - it 'parses a comment' do - expect(provider.parse_line('# dont mind me')).to eq( - line: '# dont mind me', record_type: :comment, - ) - end - - it 'parses a comment (2)' do - expect(provider.parse_line(" \t# dont mind me")).to eq( - line: " \t# dont mind me", record_type: :comment, - ) - end - - it 'allows includes' do - expect(provider.parse_line('include puppetextra')).to eq( - name: 'include', value: 'puppetextra', comment: nil, record_type: :parsed, - ) - end - - it 'allows numbers through without quotes' do - expect(provider.parse_line('wal_keep_segments = 32')).to eq( - name: 'wal_keep_segments', value: '32', comment: nil, record_type: :parsed, - ) - end - - it 'allows blanks through' do - expect(provider.parse_line('')).to eq( - line: '', record_type: :blank, - ) - end - - it 'parses keys with dots' do - expect(provider.parse_line('auto_explain.log_min_duration = 1ms')).to eq( - name: 'auto_explain.log_min_duration', value: '1ms', comment: nil, record_type: :parsed, - ) - end - end - - describe 'configuration that should be set' do - it 'sets comment lines' do - expect(provider.to_line(line: '# dont mind me', record_type: :comment)).to eq( - '# dont mind me', - ) - end - - it 'sets blank lines' do - expect(provider.to_line(line: '', record_type: :blank)).to eq( - '', - ) - end - - it 'sets simple configuration' do - expect(provider.to_line(name: 'listen_addresses', value: '*', comment: nil, record_type: :parsed)).to eq( - "listen_addresses = '*'", - ) - end - - it 'sets simple configuration with period in name' do - expect(provider.to_line(name: 'auto_explain.log_min_duration', value: '100ms', comment: nil, record_type: :parsed)).to eq( - 'auto_explain.log_min_duration = 100ms', - ) - end - - it 'sets simple configuration even with comments' do - expect(provider.to_line(name: 'listen_addresses', value: '*', comment: 'dont mind me', record_type: :parsed)).to eq( - "listen_addresses = '*' # dont mind me", - ) - end - - it 'quotes includes' do - expect(provider.to_line(name: 'include', value: 'puppetextra', comment: nil, record_type: :parsed)).to eq( - "include 'puppetextra'", - ) - end - - it 'quotes multiple words' do - expect(provider.to_line(name: 'archive_command', value: 'rsync up', comment: nil, record_type: :parsed)).to eq( - "archive_command = 'rsync up'", - ) - end - - it 'does not quote numbers' do - expect(provider.to_line(name: 'wal_segments', value: '32', comment: nil, record_type: :parsed)).to eq( - 'wal_segments = 32', - ) - end - - it 'allows numbers' do - expect(provider.to_line(name: 'integer', value: 42, comment: nil, record_type: :parsed)).to eq( - 'integer = 42', - ) - end - - it 'allows floats' do - expect(provider.to_line(name: 'float', value: 2.71828182845, comment: nil, record_type: :parsed)).to eq( - 'float = 2.71828182845', - ) - end - - it 'quotes single string address' do - expect(provider.to_line(name: 'listen_addresses', value: '0.0.0.0', comment: nil, record_type: :parsed)).to eq( - "listen_addresses = '0.0.0.0'", - ) - end - - it 'quotes an array of addresses' do - expect(provider.to_line(name: 'listen_addresses', value: ['0.0.0.0', '127.0.0.1'], comment: nil, record_type: :parsed)).to eq( - "listen_addresses = '0.0.0.0, 127.0.0.1'", - ) - end - end -end diff --git a/spec/unit/provider/postgresql_conf/ruby_spec.rb b/spec/unit/provider/postgresql_conf/ruby_spec.rb new file mode 100644 index 0000000000..11800b0fc7 --- /dev/null +++ b/spec/unit/provider/postgresql_conf/ruby_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' +provider_class = Puppet::Type.type(:postgresql_conf).provider(:ruby) + +describe provider_class do + let(:resource) { Puppet::Type.type(:postgresql_conf).new(name: 'foo', value: 'bar') } + let(:provider) { resource.provider } + + before(:each) do + allow(provider).to receive(:file_path).and_return('/tmp/foo') + allow(provider).to receive(:read_file).and_return('foo = bar') + allow(provider).to receive(:write_file).and_return(true) + end + # rubocop:enable RSpec/ReceiveMessages + + it 'has a method parse_config' do + expect(provider).to respond_to(:parse_config) + end + + it 'has a method delete_header' do + expect(provider).to respond_to(:delete_header) + end + + it 'has a method add_header' do + expect(provider).to respond_to(:add_header) + end + + it 'has a method exists?' do + expect(provider).to respond_to(:exists?) + end + + it 'has a method create' do + expect(provider).to respond_to(:create) + end + + it 'has a method destroy' do + expect(provider).to respond_to(:destroy) + end + + it 'has a method value' do + expect(provider).to respond_to(:value) + end + + it 'has a method value=' do + expect(provider).to respond_to(:value=) + end + + it 'has a method comment' do + expect(provider).to respond_to(:comment) + end + + it 'has a method comment=' do + expect(provider).to respond_to(:comment=) + end + + it 'is an instance of the Provider Ruby' do + expect(provider).to be_an_instance_of Puppet::Type::Postgresql_conf::ProviderRuby + end +end diff --git a/spec/unit/type/postgresql_conf_spec.rb b/spec/unit/type/postgresql_conf_spec.rb index 179c369740..9ce4269bfa 100644 --- a/spec/unit/type/postgresql_conf_spec.rb +++ b/spec/unit/type/postgresql_conf_spec.rb @@ -24,13 +24,13 @@ end describe 'when validating attributes' do - [:name, :provider].each do |param| + [:name, :provider, :target].each do |param| it "has a #{param} parameter" do expect(described_class.attrtype(param)).to eq(:param) end end - [:value, :target].each do |property| + [:value, :comment].each do |property| it "has a #{property} property" do expect(described_class.attrtype(property)).to eq(:property) end