From ca1997ec5dfa7b746e80ee6d9fd5f1b1caf0e1e3 Mon Sep 17 00:00:00 2001 From: Felipe Monteiro Date: Thu, 16 Aug 2018 22:18:40 +0100 Subject: [PATCH] Support rolling back to revision 0 This patch set allows revision rollback functionality to work with revision 0 in order to allow Deckhand to create a blank revision (without any documents), without having to delete every revision in the database (which is currently done here: [1] for example). This is less than ideal as revision history is lost. Instead, Deckhand should just support rolling back to revision 0 which simulates a blank slate (though in reality the previous revision history is retained). [1] https://github.com/openstack/airship-shipyard/blob/18ed6674d27ad80c87156effbd0f27d756c0bd8b/src/bin/shipyard_airflow/shipyard_airflow/control/helpers/configdocs_helper.py#L159 Change-Id: Ic94b25ca15f88ba492e08c907d4a330aeecca810 --- deckhand/db/sqlalchemy/api.py | 22 ++++++++----- .../tests/unit/db/test_revision_rollback.py | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/deckhand/db/sqlalchemy/api.py b/deckhand/db/sqlalchemy/api.py index 0a6b78c5..08f1a0d9 100644 --- a/deckhand/db/sqlalchemy/api.py +++ b/deckhand/db/sqlalchemy/api.py @@ -510,20 +510,22 @@ def revision_get_latest(session=None): :param session: Database session object. :returns: Dictionary representation of latest revision. - :raises RevisionNotFound: if the latest revision was not found. """ session = session or get_session() latest_revision = session.query(models.Revision)\ .order_by(models.Revision.created_at.desc())\ .first() - if not latest_revision: - raise errors.RevisionNotFound(revision_id='latest') - latest_revision = latest_revision.to_dict() - - latest_revision['documents'] = _update_revision_history( - latest_revision['documents']) + if latest_revision: + latest_revision = latest_revision.to_dict() + latest_revision['documents'] = _update_revision_history( + latest_revision['documents']) + else: + # If the latest revision doesn't exist, assume an empty revision + # history and return a dummy revision instead for the purposes of + # revision rollback. + latest_revision = {'documents': [], 'id': 0} return latest_revision @@ -976,7 +978,11 @@ def revision_rollback(revision_id, latest_revision, session=None): LOG.debug('The revision being rolled back to is the current revision.' 'Expect no meaningful changes.') - orig_revision = revision_get(revision_id, session=session) + if revision_id == 0: + # Placeholder revision as revision_id=0 doesn't exist. + orig_revision = {'documents': []} + else: + orig_revision = revision_get(revision_id, session=session) # A mechanism for determining whether a particular document has changed # between revisions. Keyed with the document_id, the value is True if diff --git a/deckhand/tests/unit/db/test_revision_rollback.py b/deckhand/tests/unit/db/test_revision_rollback.py index 0c587e56..e7ee7c58 100644 --- a/deckhand/tests/unit/db/test_revision_rollback.py +++ b/deckhand/tests/unit/db/test_revision_rollback.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from deckhand import errors from deckhand.tests import test_utils from deckhand.tests.unit.db import base @@ -98,3 +99,33 @@ class TestRevisionRollback(base.TestDbBase): key=lambda d: d['created_at']), ignore=['created_at', 'updated_at', 'revision_id', 'orig_revision_id', 'id']) + + def test_rollback_to_revision_0_creates_blank_slate(self): + """Rolling back to revision 0 should create an empty revision.""" + payload = base.DocumentFixture.get_minimal_multi_fixture(count=4) + bucket_name = test_utils.rand_name('bucket') + created_documents = self.create_documents(bucket_name, payload) + orig_revision_id = created_documents[0]['revision_id'] + + rollback_revision = self.rollback_revision(0) + rollback_documents = self.list_revision_documents( + rollback_revision['id'], include_history=False) + self.assertEqual(orig_revision_id + 1, rollback_revision['id']) + self.assertEmpty(rollback_documents) + + def test_rollback_to_revision_0_with_empty_revision_history(self): + """Validate that rolling back to revision_id 0 should work with + an empty revision history (zero existing revisions in the DB). + """ + rollback_revision = self.rollback_revision(0) + rollback_documents = self.list_revision_documents( + rollback_revision['id'], include_history=False) + self.assertEqual(1, rollback_revision['id']) + self.assertEmpty(rollback_documents) + + +class TestRevisionRollbackNegative(base.TestDbBase): + + def test_rollback_to_missing_revision_raises_exc(self): + # revision_id=1 doesn't exist yet since we start from an empty DB. + self.assertRaises(errors.RevisionNotFound, self.rollback_revision, 1)