Add order plugin metadata entity and logic

To support certificate plugin workflows, plugins needs a means to persist
their state in between method invocations by Barbican core. This CR adds
that persistence feature, by adding a new SQLAlchemy model and migration file,
and a new repository.

Change-Id: Ic0f74d49ab8c97e5b15c61fbc8c64d00bd9c8b5e
Implements: blueprint add-ssl-ca-support
This commit is contained in:
jfwood 2014-08-21 14:26:58 -05:00
parent b293d11c56
commit 78dfbc6463
6 changed files with 137 additions and 13 deletions

View File

@ -0,0 +1,39 @@
"""Add orders plugin metadata table and relationships
Revision ID: 4070806f6972
Revises: 47b69e523451
Create Date: 2014-08-21 14:06:48.237701
"""
# revision identifiers, used by Alembic.
revision = '4070806f6972'
down_revision = '47b69e523451'
from alembic import op
import sqlalchemy as sa
def upgrade():
ctx = op.get_context()
con = op.get_bind()
table_exists = ctx.dialect.has_table(con.engine, 'order_plugin_metadata')
if not table_exists:
op.create_table(
'order_plugin_metadata',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('order_id', sa.String(length=36), nullable=False),
sa.Column('key', sa.String(length=255), nullable=False),
sa.Column('value', sa.String(length=255), nullable=False),
sa.ForeignKeyConstraint(['order_id'], ['orders.id'],),
sa.PrimaryKeyConstraint('id'),
)
def downgrade():
op.drop_table('order_plugin_metadata')

View File

@ -43,10 +43,9 @@ CONF.register_opts(db_opts)
def init_config(sql_url=None):
"""Initialize and return the Alembic configuration."""
sqlalchemy_url = sql_url or CONF.sql_connection
if 'sqlite' in sqlalchemy_url:
LOG.warn('!!! No support for migrating sqlite databases...'
'skipping migration processing !!!')
return None
if sqlalchemy_url and 'sqlite' in sqlalchemy_url:
LOG.warn('!!! Limited support for migration commands using sqlite'
' databases; This operation may not succeed.')
config = alembic_config.Config(
os.path.join(os.path.dirname(__file__), 'alembic.ini')

View File

@ -268,11 +268,11 @@ class Secret(BASE, ModelBase):
# Eager load this relationship via 'lazy=False'.
encrypted_data = orm.relationship("EncryptedDatum", lazy=False)
secret_store_metadata = orm.\
relationship("SecretStoreMetadatum",
collection_class=col.attribute_mapped_collection('key'),
backref="secret",
cascade="all, delete-orphan")
secret_store_metadata = orm.relationship(
"SecretStoreMetadatum",
collection_class=col.attribute_mapped_collection('key'),
backref="secret",
cascade="all, delete-orphan")
def __init__(self, parsed_request=None):
"""Creates secret from a dict."""
@ -453,6 +453,17 @@ class Order(BASE, ModelBase):
container_id = sa.Column(sa.String(36), sa.ForeignKey('containers.id'),
nullable=True)
order_plugin_metadata = orm.relationship(
"OrderPluginMetadatum",
collection_class=col.attribute_mapped_collection('key'),
backref="order",
cascade="all, delete-orphan")
def _do_delete_children(self, session):
"""Sub-class hook: delete children relationships."""
for k, v in self.order_plugin_metadata.items():
v.delete(session)
def _do_extra_dict_fields(self):
"""Sub-class hook method: return dict of fields."""
ret = {'secret': {'name': self.secret_name or self.secret_id,
@ -478,6 +489,40 @@ class Order(BASE, ModelBase):
return ret
class OrderPluginMetadatum(BASE, ModelBase):
"""Represents Order plugin metadatum for a single key-value pair.
This entity is used to store plugin-specific metadata on behalf of an
Order instance.
"""
__tablename__ = "order_plugin_metadata"
order_id = sa.Column(sa.String(36), sa.ForeignKey('orders.id'),
nullable=False)
key = sa.Column(sa.String(255), nullable=False)
value = sa.Column(sa.String(255), nullable=False)
def __init__(self, key, value):
super(OrderPluginMetadatum, self).__init__()
msg = ("Must supply non-None {0} argument "
"for OrderPluginMetadatum entry.")
if key is None:
raise exception.MissingArgumentError(msg.format("key"))
self.key = key
if value is None:
raise exception.MissingArgumentError(msg.format("value"))
self.value = value
def _do_extra_dict_fields(self):
"""Sub-class hook method: return dict of fields."""
return {'key': self.key,
'value': self.value}
class Container(BASE, ModelBase):
"""Represents a Container for Secrets in the datastore.
@ -622,7 +667,7 @@ class TransportKey(BASE, ModelBase):
# Keep this tuple synchronized with the models in the file
MODELS = [TenantSecret, Tenant, Secret, EncryptedDatum, Order, Container,
ContainerConsumerMetadatum, ContainerSecret, TransportKey,
SecretStoreMetadatum, KEKDatum]
SecretStoreMetadatum, OrderPluginMetadatum, KEKDatum]
def register_models(engine):

View File

@ -264,6 +264,8 @@ class Repositories(object):
self._set_repo('secret_meta_repo', SecretStoreMetadatumRepo,
kwargs)
self._set_repo('order_repo', OrderRepo, kwargs)
self._set_repo('order_plugin_meta_repo', OrderPluginMetadatumRepo,
kwargs)
self._set_repo('transport_key_repo', TransportKeyRepo, kwargs)
def _set_repo(self, repo_name, repo_cls, specs):
@ -803,6 +805,42 @@ class OrderRepo(BaseRepo):
pass
class OrderPluginMetadatumRepo(BaseRepo):
"""Repository for the OrderPluginMetadatum entity (that stores key/value
plugin information on behalf of a Order).
"""
def save(self, metadata, order_model):
"""Saves the the specified metadata for the order.
:raises NotFound if entity does not exist.
"""
now = timeutils.utcnow()
session = get_session()
with session.begin():
for k, v in metadata.items():
meta_model = models.OrderPluginMetadatum(k, v)
meta_model.updated_at = now
meta_model.order = order_model
meta_model.save(session=session)
def _do_entity_name(self):
"""Sub-class hook: return entity name, such as for debugging."""
return "OrderPluginMetadatum"
def _do_create_instance(self):
return models.OrderPluginMetadatum()
def _do_build_get_query(self, entity_id, keystone_id, session):
"""Sub-class hook: build a retrieve query."""
query = session.query(models.OrderPluginMetadatum)
return query.filter_by(id=entity_id)
def _do_validate(self, values):
"""Sub-class hook: validate values."""
pass
class ContainerRepo(BaseRepo):
"""Repository for the Container entity."""

View File

@ -53,7 +53,7 @@ def issue_certificate_request(order_model, repos):
def _get_plugin_meta(order_model):
if order_model:
meta_dict = dict((k, v.value) for (k, v) in
order_model.order_plugin_meta.items())
order_model.order_plugin_metadata.items())
return meta_dict
else:
return dict()

View File

@ -97,8 +97,11 @@ def main():
LOG = log.getLogger(__name__)
LOG.debug("Performing database schema migration...")
dm = DatabaseManager()
dm.execute()
try:
dm = DatabaseManager()
dm.execute()
except:
LOG.exception('Problem trying to execute Alembic commands')
if __name__ == '__main__':