Added database management 58/158/4
authorJosh Farwell <jfarwell@linuxfoundation.org>
Tue, 16 Jun 2015 22:35:19 +0000 (15:35 -0700)
committerJosh Farwell <jfarwell@linuxfoundation.org>
Wed, 17 Jun 2015 15:52:41 +0000 (08:52 -0700)
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 <jfarwell@linuxfoundation.org>
.fixtures.yml
manifests/core.pp
manifests/core/config.pp
manifests/core/config/db.pp [new file with mode: 0644]
manifests/db/postgres.pp [new file with mode: 0644]
manifests/db/sqlite.pp [new file with mode: 0644]
manifests/params.pp
spec/classes/core__config__database_spec.rb [new file with mode: 0644]
spec/classes/core__config_spec.rb
spec/classes/db__postgres_spec.rb [new file with mode: 0644]
spec/classes/db__sqlite_spec.rb [new file with mode: 0644]

index 077a5e0..1615b00 100644 (file)
@@ -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}"
index 3204d6f..32259ac 100644 (file)
@@ -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'] ->
index 5634882..25e8836 100644 (file)
 # 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 (file)
index 0000000..ca7f1f2
--- /dev/null
@@ -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 <jfarwell@linuxfoundation.org>
+#
+# === 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 (file)
index 0000000..e3d9382
--- /dev/null
@@ -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 <jfarwell@linuxfoundation.org>
+#
+# === 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 (file)
index 0000000..e09585c
--- /dev/null
@@ -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 <jfarwell@linuxfoundation.org>
+#
+# === 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)
+  }
+}
index df6757f..7613209 100644 (file)
@@ -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 (file)
index 0000000..fc604f3
--- /dev/null
@@ -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
index f142b23..0a2a028 100644 (file)
@@ -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 (file)
index 0000000..ce08137
--- /dev/null
@@ -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 (file)
index 0000000..206865d
--- /dev/null
@@ -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
+
+