Merge "Keep deleted domains in the DB"
This commit is contained in:
commit
83b331e561
@ -13,8 +13,11 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import object_mapper
|
||||
from sqlalchemy.types import CHAR
|
||||
from designate.openstack.common import timeutils
|
||||
from designate import exceptions
|
||||
|
||||
|
||||
@ -82,3 +85,15 @@ class Base(object):
|
||||
if not k[0] == '_'])
|
||||
local.update(joined)
|
||||
return local.iteritems()
|
||||
|
||||
|
||||
class SoftDeleteMixin(object):
|
||||
deleted = Column(CHAR(32), nullable=False, default="0")
|
||||
deleted_at = Column(DateTime, nullable=True, default=None)
|
||||
|
||||
def soft_delete(self, session=None):
|
||||
""" Mark this object as deleted. """
|
||||
self.deleted = self.id
|
||||
self.deleted_at = timeutils.utcnow()
|
||||
|
||||
self.save(session=session)
|
||||
|
@ -21,6 +21,7 @@ from designate.openstack.common import log as logging
|
||||
from designate import exceptions
|
||||
from designate.storage import base
|
||||
from designate.storage.impl_sqlalchemy import models
|
||||
from designate.sqlalchemy.models import SoftDeleteMixin
|
||||
from designate.sqlalchemy.session import get_session
|
||||
from designate.sqlalchemy.session import get_engine
|
||||
from designate.sqlalchemy.session import SQLOPTS
|
||||
@ -64,6 +65,16 @@ class SQLAlchemyStorage(base.Storage):
|
||||
|
||||
return query
|
||||
|
||||
def _apply_deleted_criteria(self, context, model, query):
|
||||
if issubclass(model, SoftDeleteMixin):
|
||||
if context.show_deleted:
|
||||
LOG.debug('Including deleted items in query results')
|
||||
else:
|
||||
LOG.debug('Filtering deleted items from query results')
|
||||
query = query.filter(model.deleted == "0")
|
||||
|
||||
return query
|
||||
|
||||
# Quota Methods
|
||||
def create_quota(self, context, values):
|
||||
quota = models.Quota()
|
||||
@ -281,6 +292,19 @@ class SQLAlchemyStorage(base.Storage):
|
||||
return self.session.query(distinct(models.Domain.tenant_id)).count()
|
||||
|
||||
# Domain Methods
|
||||
def _find_domains(self, context, criterion, one=False):
|
||||
query = self.session.query(models.Domain)
|
||||
query = self._apply_criterion(models.Domain, query, criterion)
|
||||
query = self._apply_deleted_criteria(context, models.Domain, query)
|
||||
|
||||
if one:
|
||||
try:
|
||||
return query.one()
|
||||
except (exc.NoResultFound, exc.MultipleResultsFound):
|
||||
raise exceptions.DomainNotFound()
|
||||
else:
|
||||
return query.all()
|
||||
|
||||
def create_domain(self, context, values):
|
||||
domain = models.Domain()
|
||||
|
||||
@ -294,54 +318,26 @@ class SQLAlchemyStorage(base.Storage):
|
||||
return dict(domain)
|
||||
|
||||
def get_domains(self, context, criterion=None):
|
||||
query = self.session.query(models.Domain)
|
||||
query = self._apply_criterion(models.Domain, query, criterion)
|
||||
domains = self._find_domains(context, criterion)
|
||||
|
||||
try:
|
||||
result = query.all()
|
||||
except exc.NoResultFound:
|
||||
LOG.debug('No results found')
|
||||
return []
|
||||
else:
|
||||
return [dict(o) for o in result]
|
||||
|
||||
def _get_domain(self, context, domain_id):
|
||||
query = self.session.query(models.Domain)
|
||||
|
||||
domain = query.get(domain_id)
|
||||
|
||||
if not domain:
|
||||
raise exceptions.DomainNotFound(domain_id)
|
||||
else:
|
||||
return domain
|
||||
return [dict(d) for d in domains]
|
||||
|
||||
def get_domain(self, context, domain_id):
|
||||
domain = self._get_domain(context, domain_id)
|
||||
domain = self._find_domains(context, {'id': domain_id}, True)
|
||||
|
||||
return dict(domain)
|
||||
|
||||
def _find_domains(self, context, criterion, one=False):
|
||||
query = self.session.query(models.Domain)
|
||||
query = self._apply_criterion(models.Domain, query, criterion)
|
||||
|
||||
if one:
|
||||
try:
|
||||
domain = query.one()
|
||||
return dict(domain)
|
||||
except (exc.NoResultFound, exc.MultipleResultsFound):
|
||||
raise exceptions.DomainNotFound()
|
||||
else:
|
||||
domains = query.all()
|
||||
return [dict(d) for d in domains]
|
||||
|
||||
def find_domains(self, context, criterion):
|
||||
return self._find_domains(context, criterion)
|
||||
domains = self._find_domains(context, criterion)
|
||||
|
||||
return [dict(d) for d in domains]
|
||||
|
||||
def find_domain(self, context, criterion):
|
||||
return self._find_domains(context, criterion, one=True)
|
||||
domain = self._find_domains(context, criterion, one=True)
|
||||
return dict(domain)
|
||||
|
||||
def update_domain(self, context, domain_id, values):
|
||||
domain = self._get_domain(context, domain_id)
|
||||
domain = self._find_domains(context, {'id': domain_id}, True)
|
||||
|
||||
domain.update(values)
|
||||
|
||||
@ -353,13 +349,15 @@ class SQLAlchemyStorage(base.Storage):
|
||||
return dict(domain)
|
||||
|
||||
def delete_domain(self, context, domain_id):
|
||||
domain = self._get_domain(context, domain_id)
|
||||
domain = self._find_domains(context, {'id': domain_id}, True)
|
||||
|
||||
domain.delete(self.session)
|
||||
domain.soft_delete(self.session)
|
||||
|
||||
def count_domains(self, context, criterion=None):
|
||||
query = self.session.query(models.Domain)
|
||||
query = self._apply_criterion(models.Domain, query, criterion)
|
||||
query = self._apply_deleted_criteria(context, models.Domain, query)
|
||||
|
||||
return query.count()
|
||||
|
||||
# Record Methods
|
||||
|
@ -0,0 +1,64 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@hp.com>
|
||||
#
|
||||
# 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.
|
||||
from sqlalchemy import MetaData, Table, Column, DateTime
|
||||
from sqlalchemy.types import CHAR
|
||||
from migrate.changeset.constraint import UniqueConstraint
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
|
||||
domains_table = Table('domains', meta, autoload=True)
|
||||
|
||||
# Create the new columns
|
||||
deleted_column = Column('deleted', CHAR(32), nullable=False, default="0")
|
||||
deleted_column.create(domains_table, populate_default=True)
|
||||
|
||||
deleted_at_column = Column('deleted_at', DateTime, nullable=True,
|
||||
default=None)
|
||||
deleted_at_column.create(domains_table, populate_default=True)
|
||||
|
||||
# Drop the old single column unique
|
||||
domains_table.c.name.alter(unique=False)
|
||||
|
||||
# Add a new multi-column unique index
|
||||
constraint = UniqueConstraint('name', 'deleted', name='unique_domain_name',
|
||||
table=domains_table)
|
||||
constraint.create()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
|
||||
domains_table = Table('domains', meta, autoload=True)
|
||||
|
||||
# Drop the multi-column unique index
|
||||
constraint = UniqueConstraint('name', 'deleted', name='unique_domain_name',
|
||||
table=domains_table)
|
||||
constraint.drop()
|
||||
|
||||
# Revert to single column unique
|
||||
domains_table.c.name.alter(unique=True)
|
||||
|
||||
# Drop the deleted columns
|
||||
deleted_column = Column('deleted', CHAR(32), nullable=True, default=None)
|
||||
deleted_column.drop(domains_table)
|
||||
|
||||
deleted_at_column = Column('deleted_at', DateTime, nullable=True,
|
||||
default=None)
|
||||
deleted_at_column.drop(domains_table)
|
@ -25,6 +25,7 @@ from designate.openstack.common import timeutils
|
||||
from designate.openstack.common.uuidutils import generate_uuid
|
||||
from designate.sqlalchemy.types import UUID
|
||||
from designate.sqlalchemy.models import Base as CommonBase
|
||||
from designate.sqlalchemy.models import SoftDeleteMixin
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -66,12 +67,15 @@ class Server(Base):
|
||||
name = Column(String(255), nullable=False, unique=True)
|
||||
|
||||
|
||||
class Domain(Base):
|
||||
class Domain(SoftDeleteMixin, Base):
|
||||
__tablename__ = 'domains'
|
||||
__table_args__ = (
|
||||
UniqueConstraint('name', 'deleted', name='unique_domain_name'),
|
||||
)
|
||||
|
||||
tenant_id = Column(String(36), default=None, nullable=True)
|
||||
|
||||
name = Column(String(255), nullable=False, unique=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
email = Column(String(255), nullable=False)
|
||||
|
||||
ttl = Column(Integer, default=3600, nullable=False)
|
||||
|
@ -584,6 +584,15 @@ class StorageTestCase(TestCase):
|
||||
uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b'
|
||||
self.storage.get_domain(self.admin_context, uuid)
|
||||
|
||||
def test_get_deleted_domain(self):
|
||||
context = self.get_admin_context()
|
||||
context.show_deleted = True
|
||||
|
||||
_, domain = self.create_domain()
|
||||
|
||||
self.storage.delete_domain(self.admin_context, domain['id'])
|
||||
self.storage.get_domain(context, domain['id'])
|
||||
|
||||
def test_find_domain_criterion(self):
|
||||
_, domain_one = self.create_domain(0)
|
||||
_, domain_two = self.create_domain(1)
|
||||
|
Loading…
Reference in New Issue
Block a user