Merge "Remove built-in plugins from database schema"

This commit is contained in:
Zuul 2019-01-09 22:12:09 +00:00 committed by Gerrit Code Review
commit 96e332b130
5 changed files with 176 additions and 9 deletions

View File

@ -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'}]))

View File

@ -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

View File

View File

@ -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')

View File

@ -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.