From ddc6d1c4edf49c74458c46c3bda9408c68a48870 Mon Sep 17 00:00:00 2001 From: Josh Farwell Date: Tue, 16 Jun 2015 15:35:19 -0700 Subject: [PATCH] Added database management Core::config now parses the config file information for database settings. If postgres, exports a resource for creating a postgres database. TODO: if sqlite, it will verify that the mailman3 user can write to the database directory. If mysql, fails, as it is not officially supported by the project at this time. Change-Id: Ie91cc0ff612e382967b5b3f1661bd301c5143077 Signed-off-by: Josh Farwell --- .fixtures.yml | 1 + manifests/core.pp | 3 + manifests/core/config.pp | 13 ++- manifests/core/config/db.pp | 109 ++++++++++++++++++++++ manifests/db/postgres.pp | 70 ++++++++++++++ manifests/db/sqlite.pp | 49 ++++++++++ manifests/params.pp | 3 +- spec/classes/core__config__database_spec.rb | 140 ++++++++++++++++++++++++++++ spec/classes/core__config_spec.rb | 3 + spec/classes/db__postgres_spec.rb | 38 ++++++++ spec/classes/db__sqlite_spec.rb | 46 +++++++++ 11 files changed, 473 insertions(+), 2 deletions(-) create mode 100644 manifests/core/config/db.pp create mode 100644 manifests/db/postgres.pp create mode 100644 manifests/db/sqlite.pp create mode 100644 spec/classes/core__config__database_spec.rb create mode 100644 spec/classes/db__postgres_spec.rb create mode 100644 spec/classes/db__sqlite_spec.rb diff --git a/.fixtures.yml b/.fixtures.yml index 077a5e0..1615b00 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -5,5 +5,6 @@ fixtures: repositories: firewall: "git://github.com/puppetlabs/puppetlabs-firewall.git" stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git" + postgresql: "git://github.com/puppetlabs/puppetlabs-postgresql.git" symlinks: mailman3: "#{source_dir}" diff --git a/manifests/core.pp b/manifests/core.pp index 3204d6f..32259ac 100644 --- a/manifests/core.pp +++ b/manifests/core.pp @@ -42,10 +42,12 @@ class mailman3::core ( $core_manage_firewall = $mailman3::params::core_manage_firewall, $core_override_options = {}, $core_override_allowed_hosts = {}, + $db_tag = $mailman3::params::core_db_tag, ) inherits mailman3::params { # Make sure parameters are properly formatted validate_bool($install_core) + validate_string($db_tag) validate_string($core_version) validate_bool($core_manage_database) validate_bool($core_manage_firewall) @@ -72,6 +74,7 @@ class mailman3::core ( core_manage_firewall => $core_manage_firewall, core_options => $core_options, core_allowed_hosts => $core_allowed_hosts, + db_tag => $db_tag, } Anchor['mailman3::core::begin'] -> diff --git a/manifests/core/config.pp b/manifests/core/config.pp index 5634882..25e8836 100644 --- a/manifests/core/config.pp +++ b/manifests/core/config.pp @@ -36,11 +36,13 @@ # Copyright 2015 Josh Farwell, unless otherwise noted. # class mailman3::core::config ( + $core_allowed_hosts, $core_manage_database, $core_manage_firewall, $core_options, - $core_allowed_hosts, + $db_tag, ) { + validate_string($db_tag) validate_bool($core_manage_database) validate_bool($core_manage_firewall) validate_hash($core_options) @@ -65,4 +67,13 @@ class mailman3::core::config ( core_options => $core_options, } } + + if ($core_manage_database) { + class { 'mailman3::core::config::db': + core_manage_database => $core_manage_database, + core_options => $core_options, + db_tag => $db_tag, + } + } + } diff --git a/manifests/core/config/db.pp b/manifests/core/config/db.pp new file mode 100644 index 0000000..ca7f1f2 --- /dev/null +++ b/manifests/core/config/db.pp @@ -0,0 +1,109 @@ +# == Class: mailman3::core::config::db +# +# Full description of class mailman3 here. +# +# === Parameters +# +# Document parameters here. +# +# [*sample_parameter*] +# Explanation of what this parameter affects and what it defaults to. +# e.g. "Specify one or more upstream ntp servers as an array." +# +# === Variables +# +# Here you should define a list of variables that this module would require. +# +# [*sample_variable*] +# Explanation of how this variable affects the function of this class and if +# it has a default. e.g. "The parameter enc_ntp_servers must be set by the +# External Node Classifier as a comma separated list of hostnames." (Note, +# global variables should be avoided in favor of class parameters as +# of Puppet 2.6.) +# +# === Examples +# +# class { 'mailman3': +# servers => [ 'pool.ntp.org', 'ntp.local.company.com' ], +# } +# +# === Authors +# +# Josh Farwell +# +# === Copyright +# +# Copyright 2015 Josh Farwell, unless otherwise noted. +# +class mailman3::core::config::db ( + $core_manage_database, + $core_options, + $db_tag, +) { + validate_string($db_tag) + validate_bool($core_manage_database) + validate_hash($core_options) + + if has_key($core_options, 'database') { + + validate_hash($core_options['database']) + + # string manipulation to parse our options from SQLAlchemy URL + unless has_key($core_options['database'], 'url') { + fail 'Database hash has no url' + } + + validate_re($core_options['database']['url'], '^*://*') + + $db_url_arr = split($core_options['database']['url'], '://') + $db_type = $db_url_arr[0] + + case $db_type { + /^sqlite/: { + $db_path = $db_url_arr[1] + + class { 'mailman3::db::sqlite': + manage_database => $core_manage_database, + db_path => $db_path + } + } + + /^mysql/: { + fail 'MySQL is not officially supported by Mailman 3 at this time.' + + #$db_options_arr = split($db_url_arr[1], '[:@/]') + + #$db_options = { + #'username' => $db_options_arr[0], + #'password' => $db_options_arr[1], + #'hostname' => $db_options_arr[2], + #'database' => $db_options_arr[3], + #} + + #class { '::mailman3::db::mysql': + #db_tag => $db_tag, + #manage_database => $core_manage_database, + #options => $core_options, + #} + } + /^postgres/: { + $db_database_arr = split($db_url_arr[1], '/') + $db_options_arr = split($db_url_arr[1], '[:@/]') + + $db_options = { + 'username' => $db_options_arr[0], + 'password' => $db_options_arr[1], + 'hostname' => $db_options_arr[2], + 'database' => $db_options_arr[3], + } + + class { 'mailman3::db::postgres': + db_tag => $db_tag, + manage_database => $core_manage_database, + db_options => $db_options, + } + } + default: { } + } + } +} diff --git a/manifests/db/postgres.pp b/manifests/db/postgres.pp new file mode 100644 index 0000000..e3d9382 --- /dev/null +++ b/manifests/db/postgres.pp @@ -0,0 +1,70 @@ +# == Class: mailman3 +# +# Full description of class mailman3 here. +# +# === Parameters +# +# Document parameters here. +# +# [*sample_parameter*] +# Explanation of what this parameter affects and what it defaults to. +# e.g. "Specify one or more upstream ntp servers as an array." +# +# === Variables +# +# Here you should define a list of variables that this module would require. +# +# [*sample_variable*] +# Explanation of how this variable affects the function of this class and if +# it has a default. e.g. "The parameter enc_ntp_servers must be set by the +# External Node Classifier as a comma separated list of hostnames." (Note, +# global variables should be avoided in favor of class parameters as +# of Puppet 2.6.) +# +# === Examples +# +# class { 'mailman3': +# servers => [ 'pool.ntp.org', 'ntp.local.company.com' ], +# } +# +# === Authors +# +# Josh Farwell +# +# === Copyright +# +# Copyright 2015 Josh Farwell, unless otherwise noted. +# +class mailman3::db::postgres ( + $db_options, + $db_tag, + $manage_database, +) { + validate_string($db_tag) + validate_bool($manage_database) + validate_hash($db_options) + + if ($manage_database) { + + validate_string($db_options['username']) + validate_string($db_options['password']) + validate_string($db_options['hostname']) + validate_string($db_options['database']) + + $username = $db_options['username'] + $password = $db_options['password'] + $hostname = $db_options['hostname'] + $database = $db_options['database'] + + # export postgresql database configuration + + @@postgresql::server::db { "$::{database}_$::{fdqn}": + dbname => $database, + grant => 'ALL', + owner => $username, + password => $password, + tag => $db_tag, + user => $username, + } + } +} diff --git a/manifests/db/sqlite.pp b/manifests/db/sqlite.pp new file mode 100644 index 0000000..e09585c --- /dev/null +++ b/manifests/db/sqlite.pp @@ -0,0 +1,49 @@ +# == Class: mailman3::db::sqlite +# +# Full description of class mailman3 here. +# +# === Parameters +# +# Document parameters here. +# +# [*sample_parameter*] +# Explanation of what this parameter affects and what it defaults to. +# e.g. "Specify one or more upstream ntp servers as an array." +# +# === Variables +# +# Here you should define a list of variables that this module would require. +# +# [*sample_variable*] +# Explanation of how this variable affects the function of this class and if +# it has a default. e.g. "The parameter enc_ntp_servers must be set by the +# External Node Classifier as a comma separated list of hostnames." (Note, +# global variables should be avoided in favor of class parameters as +# of Puppet 2.6.) +# +# === Examples +# +# class { 'mailman3': +# servers => [ 'pool.ntp.org', 'ntp.local.company.com' ], +# } +# +# === Authors +# +# Josh Farwell +# +# === Copyright +# +# Copyright 2015 Josh Farwell, unless otherwise noted. +# +class mailman3::db::sqlite ( + $db_path, + $manage_database, +) { + validate_bool($manage_database) + + if ($manage_database) { + # TODO: extract containing directory from sqlite db path and ensure + # that it exists and the mailman3 user can read and write there. + validate_string($db_path) + } +} diff --git a/manifests/params.pp b/manifests/params.pp index df6757f..7613209 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -49,6 +49,7 @@ class mailman3::params { $rest_admin_pass = 'restpass' # Mailman Core values + $core_db_tag = 'mailman3-core' $core_layout = 'fhs' $core_bin_dir = '/usr/libexec/mailman3' $core_var_dir = '/var/lib/mailman3' @@ -60,7 +61,7 @@ class mailman3::params { $core_site_owner = 'root@localhost' $core_version = 'installed' # could also be 3.0.0 - # Mailman Core default options + # Mailman Core default options hashes $core_default_allowed_hosts = { 'rest' => ['0.0.0.0'], 'lmtp' => ['0.0.0.0'], diff --git a/spec/classes/core__config__database_spec.rb b/spec/classes/core__config__database_spec.rb new file mode 100644 index 0000000..fc604f3 --- /dev/null +++ b/spec/classes/core__config__database_spec.rb @@ -0,0 +1,140 @@ +require 'spec_helper' + +describe 'mailman3::core::config::db', :type => :class do + + # since we have no defaults params, should fail + context 'with defaults for all parameters' do + let (:params) {{}} + + it do + expect { + should compile + }.to raise_error(RSpec::Expectations::ExpectationNotMetError, + /Must pass /) + end + end + + + # with defaults from params.pp + context 'with assumed default parameters' do + let (:params) { { + 'db_tag' => 'mailman3-core', + 'core_manage_database' => true, + 'core_options' => {}, + } } + + # defaults should do nothing because the application takes + # care of it. This is the only class defined in the manifest. + it { is_expected.to_not contain_class('mailman3::db::postgres') } + end + + + context 'with management flag set to false' do + let (:params) {{ + 'db_tag' => '', + 'core_manage_database' => false, + 'core_options' => {} + }} + + it { is_expected.to_not contain_class('mailman3::db::postgres') } + end + + + context 'with a MySQL URL in options' do + let (:params) {{ + 'db_tag' => 'testtag', + 'core_manage_database' => true, + 'core_options' => { + 'database' => { + 'url' => 'mysql://test:test@test/test', + } + } + }} + + it do + expect { + should compile + }.to raise_error( + /MySQL is not officially supported/) + end + end + + + context 'with a SQLite URL in options' do + let (:params) {{ + 'db_tag' => 'testtag', + 'core_manage_database' => true, + 'core_options' => { + 'database' => { + 'url' => 'sqlite:///path/to/database.db', + }, + }, + }} + + it { is_expected.to_not contain_class('mailman3::db::postgres') } + it { is_expected.to contain_class('mailman3::db::sqlite').with( + 'manage_database' => true, + 'db_path' => '/path/to/database.db', + ) } + + end + + + context 'with a PostgreSQL URL in options' do + let (:params) { { + 'db_tag' => 'test', + 'core_manage_database' => true, + 'core_options' => { + 'database' => { + 'url' => 'postgres://myuser:mypassword@mypghost/mailman', + }, + }, + } } + + it { is_expected.to contain_class('mailman3::db::postgres').with( + 'db_tag' => 'test', + 'manage_database' => true, + 'db_options' => { + 'username' => 'myuser', + 'password' => 'mypassword', + 'hostname' => 'mypghost', + 'database' => 'mailman', + }, ) } + end + + context 'with a malformed database URL' do + let (:params) { { + 'db_tag' => 'test', + 'core_manage_database' => true, + 'core_options' => { + 'database' => { + 'url' => 'garbage string with garbage', + }, + }, + } } + + it do + expect { + should compile + }.to raise_error(/does not match /) + end + + end + + context 'with a database hash but no url value' do + let (:params) { { + 'db_tag' => 'test', + 'core_manage_database' => true, + 'core_options' => { + 'database' => {}, + }, + } } + + it do + expect { + should compile + }.to raise_error(/Database hash has no url/) + end + end + +end diff --git a/spec/classes/core__config_spec.rb b/spec/classes/core__config_spec.rb index f142b23..0a2a028 100644 --- a/spec/classes/core__config_spec.rb +++ b/spec/classes/core__config_spec.rb @@ -13,6 +13,7 @@ describe 'mailman3::core::config', :type => :class do context 'with assumed default parameters' do let (:params) { { + 'db_tag' => 'testtag', 'core_allowed_hosts' => { 'rest' => ['0.0.0.0'], 'lmtp' => ['0.0.0.0'], @@ -60,6 +61,7 @@ describe 'mailman3::core::config', :type => :class do context 'with user defined parameters' do let (:params) { { + 'db_tag' => 'testtag', 'core_allowed_hosts' => { 'rest' => ['10.0.0.1'], 'lmtp' => ['10.0.0.1'], @@ -108,6 +110,7 @@ describe 'mailman3::core::config', :type => :class do let (:params) { { + 'db_tag' => 'testtag', 'core_manage_database' => false, 'core_manage_firewall' => false, 'core_allowed_hosts' => {}, diff --git a/spec/classes/db__postgres_spec.rb b/spec/classes/db__postgres_spec.rb new file mode 100644 index 0000000..ce08137 --- /dev/null +++ b/spec/classes/db__postgres_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe 'mailman3::db::postgres', :type => :class do + + # known good parameters + let(:params) { + { + 'db_tag' => 'testtag', + 'manage_database' => true, + 'db_options' => { + 'username' => 'testuser', + 'password' => 'testpass', + 'hostname' => 'localhost', + 'database' => 'testdb', + } + } + } + + context 'with defaults for all parameters' do + let(:params) {{}} + + it do + expect { + should compile + }.to raise_error(RSpec::Expectations::ExpectationNotMetError, + /Must pass/) + end + end + + context 'with known good parameters' do + it { is_expected.to contain_class('mailman3::db::postgres') } + + # we unfortunately can't test for exported resources. + #it { is_expected.to contain_class('postgresql::server::db') } + end +end + + diff --git a/spec/classes/db__sqlite_spec.rb b/spec/classes/db__sqlite_spec.rb new file mode 100644 index 0000000..206865d --- /dev/null +++ b/spec/classes/db__sqlite_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe 'mailman3::db::sqlite', :type => :class do + + # known good parameters + let(:params) { + { + 'manage_database' => true, + 'db_path' => '/path/to/database.db', + } + } + + context 'with defaults for all parameters' do + let(:params) {{}} + + it do + expect { + should compile + }.to raise_error(RSpec::Expectations::ExpectationNotMetError, + /Must pass/) + end + end + + context 'with known good parameters' do + it { is_expected.to contain_class('mailman3::db::sqlite') } + + # when TODO for this class is complete it will need to test for a file resource + #it { is_expected.to contain_file('sqlite_dir') } + end + + context 'with management flag set to false' do + let(:params) { + { + 'manage_database' => false, + 'db_path' => '/path/to/database.db', + } + } + + it { is_expected.to contain_class('mailman3::db::sqlite') } + + # when TODO for this class is complete it wil need to test that a file resource is not present. + #it { is_expected.to_not contain_file('sqlite_dir') } + end +end + + -- 2.16.6