diff --git a/storyboard/plugin/email/base.py b/storyboard/plugin/email/base.py index b00259ad..5f7be575 100644 --- a/storyboard/plugin/email/base.py +++ b/storyboard/plugin/email/base.py @@ -20,7 +20,6 @@ from oslo_log import log from storyboard.plugin.base import PluginBase from storyboard.plugin.email import get_email_directory -from storyboard.plugin.email.outbox import Outbox from storyboard.plugin.email.smtp_client import get_smtp_client CONF = cfg.CONF @@ -48,13 +47,6 @@ class EmailPluginBase(PluginBase): (e,)) return False - # Assert that we can create an outbox. - try: - Outbox() - except OSError as e: - LOG.error('Cannot create mailbox, disabling plugin: %s' % (e,)) - return False - # Assert that the smtp sender can connect to the server. try: with get_smtp_client(): diff --git a/storyboard/plugin/email/outbox.py b/storyboard/plugin/email/outbox.py deleted file mode 100644 index 1feff511..00000000 --- a/storyboard/plugin/email/outbox.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. See the License for the specific language governing permissions and -# limitations under the License. - -import mailbox -import os -import six -import time -import uuid - -from oslo_config import cfg -from oslo_log import log - -from storyboard.plugin.email import get_email_directory - - -CONF = cfg.CONF -LOG = log.getLogger(__name__) - - -class get_outbox(object): - """This generator will create an instance of the email outbox, and make - sure it has been closed after use. - """ - - def __init__(self): - self.outbox = None - - def __enter__(self): - self.outbox = Outbox() - return self.outbox - - def __exit__(self, exc_type, exc_val, exc_tb): - # On a clean use, flush the outbox. - if not exc_type: - self.outbox.flush() - - # All outboxes get closed. - self.outbox.close() - self.outbox = None - - -class Outbox(mailbox.Maildir): - """Our email outbox, a place where we store our generated email messages - before our cron mailer sends them out. It is implemented using the python - email.Maildir package, because, well, it quacks like a mailbox. - """ - - def __init__(self): - """Create a new instance of our outbox.""" - working_directory = get_email_directory() - outbox_path = os.path.join(working_directory, 'outbox') - - # Explicitly set the factory to None, because py2.7 defaults this to - # rfc822, which causes the return types to be different between 3.4 - # and 2.7. - mailbox.Maildir.__init__(self, outbox_path, factory=None) - - def _create_tmp(self): - """Create a file in the tmp subdirectory and open and return it. This - is an intentional override of the parent class, in an attempt to - overcome the pid-based name generator that might cause conflicts when - there are two operating threads in a single pid. - """ - now = time.time() - uniq = six.text_type(uuid.uuid4()) - uniq = "%sM%s_%s" % (int(now), int(now % 1 * 1e6), uniq) - path = os.path.join(self._path, 'tmp', uniq) - - return mailbox._create_carefully(path) diff --git a/storyboard/tests/plugin/email/test_base.py b/storyboard/tests/plugin/email/test_base.py index 4888a881..be347d65 100644 --- a/storyboard/tests/plugin/email/test_base.py +++ b/storyboard/tests/plugin/email/test_base.py @@ -21,7 +21,6 @@ from oslo_config import cfg import mock_smtp as mock from storyboard.plugin.email.base import EmailPluginBase -from storyboard.plugin.email import get_email_directory from storyboard.tests import base @@ -33,8 +32,7 @@ PERM_ALL = stat.S_IRWXU + stat.S_IRWXG + stat.S_IRWXO class TestEmailPluginBase(base.WorkingDirTestCase): """Unit tests for the base email plugin.""" - def setup_helper(self, config=True, working_dir=True, outbox=True, - smtp=True): + def setup_helper(self, config=True, working_dir=True, smtp=True): """Setup helper: Setup the email test environment with various items enabled/disabled. """ @@ -42,10 +40,6 @@ class TestEmailPluginBase(base.WorkingDirTestCase): if not working_dir: os.chmod(CONF.working_directory, PERM_READ_ONLY) - elif not outbox: - # Can't modify this if the parent directory's already locked down. - email = get_email_directory() - os.chmod(email, PERM_READ_ONLY) if not smtp: mock.DummySMTP.exception = smtplib.SMTPException @@ -85,12 +79,6 @@ class TestEmailPluginBase(base.WorkingDirTestCase): plugin = EmailPluginBase(CONF) self.assertFalse(plugin.enabled()) - def test_no_outbox(self): - """Assert that we're not enabled when the outbox is not accessible.""" - self.setup_helper(outbox=False) - plugin = EmailPluginBase(CONF) - self.assertFalse(plugin.enabled()) - def test_no_smtp(self): """Assert that we're not enabled when smtp is misconfigured. """ diff --git a/storyboard/tests/plugin/email/test_outbox.py b/storyboard/tests/plugin/email/test_outbox.py deleted file mode 100644 index ba5aa236..00000000 --- a/storyboard/tests/plugin/email/test_outbox.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. See the License for the specific language governing permissions and -# limitations under the License. - - -import datetime -from email.message import Message -from mock import patch -import re -import six -import uuid - -from oslo_config import cfg - -from storyboard.plugin.email.factory import EmailFactory -from storyboard.plugin.email.outbox import get_outbox -from storyboard.plugin.email.outbox import Outbox -from storyboard.tests import base - - -CONF = cfg.CONF - - -class TestGetOutbox(base.WorkingDirTestCase): - def test_single_scope(self): - """Assert that get_outbox returns an Outbox. This test brought to you - by the department of redundancy department. - """ - with get_outbox() as outbox: - self.assertIsInstance(outbox, Outbox) - - @patch.object(Outbox, 'close') - def test_was_closed(self, mock_close): - """Assert that the close() method is called when the scope exits.""" - with get_outbox(): - pass - - self.assertTrue(mock_close.called) - - @patch.object(Outbox, 'flush') - def test_was_flushed(self, mock_flush): - """Assert that the flush() method is called when the scope exits.""" - with get_outbox(): - pass - - self.assertTrue(mock_flush.called) - - -class TestOutbox(base.WorkingDirTestCase): - def test_create_outbox(self): - '''Assert that we can create an outbox. At this point, given all of - the other IOError and working directory checks, we are not bothering - with additional sanity checks to make sure the outbox can be created. - If things are broken at this point, it should have already gotten - picked up. - ''' - outbox = Outbox() - self.assertIsNotNone(outbox) - - def test_basic_outbox_functions(self): - '''Assert that we can list elements of our outbox.''' - outbox = Outbox() - self.assertIsNotNone(outbox) - - factory = EmailFactory('test@example.org', - 'test_subject', - 'test.txt', - 'plugin.email') - for i in range(0, 100): - recipient = 'test_%s@example.com' % (i,) - message = factory.build(recipient, test_parameter=i) - - # This will throw an error if the message ID already exists. - outbox.add(message) - outbox.flush() - - values = [] - for key, email in six.iteritems(outbox): - # Assert that the type of the email is correct. - self.assertIsInstance(email, Message) - - # Assert that the message has a date header. While we can extract - # this from the ID, it's also helpful for us to timebox the - # emails to send by worker. - self.assertIsNotNone(email.get('Date')) - - # Make sure we only have one payload (since that's what the - # factory was configured with) - self.assertEqual(1, len(email.get_payload())) - - # Pull the ID from the payload and check it against all the - # emails we've found so far. - text_part = email.get_payload(0) - key = int(text_part.get_payload(decode=True)) - self.assertNotIn(key, values) - - # Store the key for later comparison. - values.append(key) - - self.assertEqual(100, len(values)) - - def test_uuid_ids(self): - '''Assert that our message id's are not thread-based.''' - outbox = Outbox() - self.assertIsNotNone(outbox) - - factory = EmailFactory('test@example.org', - 'test_subject', - 'test.txt', - 'plugin.email') - for i in range(0, 100): - recipient = 'test_%s@example.com' % (i,) - message = factory.build(recipient, test_parameter=i) - - # This will throw an error if the message ID already exists. - key = outbox.add(message) - parts = re.match(r'^([0-9]+)M([0-9]+)_(.*)$', key) - self.assertIsNotNone(parts) - - # The first part should be a timestamp. - timestamp = float(parts.group(1)) + (float(parts.group(2)) / 1e6) - send_date = datetime.datetime.fromtimestamp(timestamp) - self.assertIsNotNone(send_date) - - # The last part should be a UUID. Trying to construct it will - # cause a value error if it is not. - uuid.UUID(parts.group(3), version=4) - - outbox.flush()