diff --git a/monasca_api/db/alembic/versions/26083b298bb7_remove_builtin_notification_types.py b/monasca_api/db/alembic/versions/26083b298bb7_remove_builtin_notification_types.py new file mode 100644 index 000000000..ea5605a15 --- /dev/null +++ b/monasca_api/db/alembic/versions/26083b298bb7_remove_builtin_notification_types.py @@ -0,0 +1,52 @@ +# Copyright 2018 StackHPC Ltd +# +# 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. +"""Remove builtin notification types + +Revision ID: 26083b298bb7 +Revises: f69cb3152a76 +Create Date: 2018-09-18 13:52:02.170226 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.sql import table + +# revision identifiers, used by Alembic. +revision = '26083b298bb7' +down_revision = 'f69cb3152a76' +branch_labels = None +depends_on = None + +_nm_types = table( + 'notification_method_type', + sa.Column('name', + sa.String(length=20), + nullable=False)) + + +def upgrade(): + # Built-in notification types have been removed. Here, we + # remove them and rely on monasca_notification to re-populate + # the table according to what is set in its config file. + op.execute(_nm_types.delete().where( + _nm_types.c.name.in_(('EMAIL', 'WEBHOOK', 'PAGERDUTY')))) + + +def downgrade(): + # Some or all of these might be present if they have been explicitly + # enabled in monasca-notification. + op.execute(_nm_types.insert().prefix_with("IGNORE").values( + [{'name': 'EMAIL'}, + {'name': 'WEBHOOK'}, + {'name': 'PAGERDUTY'}])) diff --git a/monasca_api/db/fingerprint.py b/monasca_api/db/fingerprint.py index b566c69a3..036651f52 100644 --- a/monasca_api/db/fingerprint.py +++ b/monasca_api/db/fingerprint.py @@ -14,9 +14,16 @@ import hashlib +from oslo_log import log +from oslo_utils import encodeutils from sqlalchemy import MetaData +from sqlalchemy.orm import sessionmaker -# Map of SHA1 fingerprints to alembic revisions. +LOG = log.getLogger(__name__) + +# Map of SHA1 fingerprints to alembic revisions. Note that this is +# used in the pre-alembic case and does not need to be updated if a +# new revision is introduced. _REVS = {"43e5913b0272077321ab6f25ffbcda7149b6284b": "00597b5c8325", "c4e5c870c705421faa4041405b5a895970faa434": "0cce983d957a", "f7a79c4eea9c9d130277a64eb6d2d16587088dbb": "30181b42434b", @@ -34,8 +41,17 @@ _REVS = {"43e5913b0272077321ab6f25ffbcda7149b6284b": "00597b5c8325", class Fingerprint(object): def __init__(self, engine): - metadata = MetaData(bind=engine, reflect=True) + metadata = self._get_metadata(engine) + self.schema_raw = self._get_schema_raw(metadata) + self.sha1 = self._get_schema_sha1(self.schema_raw) + self.revision = self._get_revision(metadata, engine, self.sha1) + @staticmethod + def _get_metadata(engine): + return MetaData(bind=engine, reflect=True) + + @staticmethod + def _get_schema_raw(metadata): schema_strings = [] for table in metadata.sorted_tables: @@ -56,11 +72,39 @@ class Fingerprint(object): schema_strings.append("") - self.schema_raw = "\n".join(schema_strings) - self.sha1 = hashlib.sha1(self.schema_raw).hexdigest() + return "\n".join(schema_strings) - try: - self.revision = _REVS[self.sha1] - except KeyError: - # Fingerprint does not match any revisions - self.revision = None + @staticmethod + def _get_schema_sha1(schema_raw): + return hashlib.sha1(encodeutils.to_utf8(schema_raw)).hexdigest() + + @staticmethod + def _get_revision(metadata, engine, sha1): + # Alembic stores the current version in the DB so check that first + # and fall back to the lookup table for the pre-alembic case. + versions_table = metadata.tables.get('alembic_version') + if versions_table is not None: + return Fingerprint._lookup_version_from_db(versions_table, engine) + elif sha1: + return Fingerprint._lookup_version_from_table(sha1) + + @staticmethod + def _get_db_session(engine): + Session = sessionmaker(bind=engine) + return Session() + + @staticmethod + def _lookup_version_from_db(versions_table, engine): + session = Fingerprint._get_db_session(engine) + # This will throw an exception for the unexpected case when there is + # more than one row. The query returns a tuple which is stripped off + # before returning. + return session.query(versions_table).one()[0] + + @staticmethod + def _lookup_version_from_table(sha1): + revision = _REVS.get(sha1) + if not revision: + LOG.warning("Fingerprint: {} does not match any revisions." + .format(sha1)) + return revision diff --git a/monasca_api/tests/db/__init__.py b/monasca_api/tests/db/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monasca_api/tests/db/test_fingerprint.py b/monasca_api/tests/db/test_fingerprint.py new file mode 100644 index 000000000..7e2744b52 --- /dev/null +++ b/monasca_api/tests/db/test_fingerprint.py @@ -0,0 +1,65 @@ +# Copyright 2018 StackHPC Ltd. +# +# 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 hashlib +import mock + +import monasca_api.db.fingerprint as fingerprint +from monasca_api.tests import base + + +class TestFingerprint(base.BaseTestCase): + + @mock.patch('monasca_api.db.fingerprint.Fingerprint._get_metadata') + @mock.patch('monasca_api.db.fingerprint.Fingerprint._get_schema_raw') + def test_get_schema_raw_pre_alembic(self, mock_schema_raw, mock_metadata): + mock_schema_raw.return_value = 'dummy_schema_raw' + + tables = mock.PropertyMock = { + 'dummy_table': 'dummy_columns' + } + mock_metadata.return_value.tables = tables + + # No Alembic revision ID exists in the DB so we look it up from the + # table of fingerprints. Since we use a dummy schema, we insert a dummy + # entry into the lookup table. + fingerprint._REVS[ + hashlib.sha1(b'dummy_schema_raw').hexdigest()] = 'dummy_revision' + + f = fingerprint.Fingerprint('mock_engine') + self.assertEqual(f.schema_raw, 'dummy_schema_raw') + self.assertEqual(f.sha1, hashlib.sha1(b'dummy_schema_raw').hexdigest()) + self.assertEqual(f.revision, 'dummy_revision') + + @mock.patch('monasca_api.db.fingerprint.Fingerprint._get_db_session') + @mock.patch('monasca_api.db.fingerprint.Fingerprint._get_metadata') + @mock.patch('monasca_api.db.fingerprint.Fingerprint._get_schema_raw') + def test_get_schema_raw_post_alembic( + self, mock_schema_raw, mock_metadata, mock_db_session): + mock_schema_raw.return_value = 'dummy_schema_raw' + + tables = mock.PropertyMock = { + 'alembic_version': 'dummy_version', + 'dummy_table': 'dummy_columns' + } + mock_metadata.return_value.tables = tables + + # Alembic sets the version in the DB, so we look it up from there + mock_db_session.return_value.query.return_value.one.return_value = ( + 'dummy_revision',) + + f = fingerprint.Fingerprint('mock_engine') + self.assertEqual(f.schema_raw, 'dummy_schema_raw') + self.assertEqual(f.sha1, hashlib.sha1(b'dummy_schema_raw').hexdigest()) + self.assertEqual(f.revision, 'dummy_revision') diff --git a/releasenotes/notes/stop_pre_populating_built_in_monasca_notification_plugins_in_db-140ece49106b4a5a.yaml b/releasenotes/notes/stop_pre_populating_built_in_monasca_notification_plugins_in_db-140ece49106b4a5a.yaml new file mode 100644 index 000000000..74dfb0d14 --- /dev/null +++ b/releasenotes/notes/stop_pre_populating_built_in_monasca_notification_plugins_in_db-140ece49106b4a5a.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - The concept of built-in monasca-notification plugins has been removed + and the built-in plugins are no longer pre-populated in the database. If + you were using the PAGERDUTY, EMAIL or WEBHOOK notification plugin you + should explicitly enable it in the monasca-notification config file.