diff --git a/deckhand/db/sqlalchemy/api.py b/deckhand/db/sqlalchemy/api.py index 82e6128d..d5f07a38 100644 --- a/deckhand/db/sqlalchemy/api.py +++ b/deckhand/db/sqlalchemy/api.py @@ -1021,10 +1021,9 @@ def revision_rollback(revision_id, latest_revision, session=None): (d['data_hash'], d['metadata_hash']) for d in latest_revision['documents']] - # If the rollback revision is the same as the latest revision, then there's - # no point in rolling back. if latest_revision['id'] == revision_id: - raise errors.InvalidRollback(revision_id=revision_id) + LOG.debug('The revision being rolled back to is the current revision.' + 'Expect no meaningful changes.') orig_revision = revision_get(revision_id, session=session) @@ -1039,10 +1038,12 @@ def revision_rollback(revision_id, latest_revision, session=None): else: doc_diff[orig_doc['id']] = False - # If no changes have been made between the target revision to rollback to - # and the latest revision, raise an exception. + # No changes have been made between the target revision to rollback to + # and the latest revision. if set(doc_diff.values()) == set([False]): - raise errors.InvalidRollback(revision_id=revision_id) + LOG.debug('The revision being rolled back to has the same documents ' + 'as that of the current revision. Expect no meaningful ' + 'changes.') # Create the new revision, new_revision = models.Revision() diff --git a/deckhand/errors.py b/deckhand/errors.py index 361e4cbb..30acd8bc 100644 --- a/deckhand/errors.py +++ b/deckhand/errors.py @@ -263,12 +263,6 @@ class RevisionTagBadFormat(DeckhandException): code = 400 -class InvalidRollback(DeckhandException): - msg_fmt = ("The requested rollback for target revision %(revision)s is " - "invalid as the latest revision matches the target revision.") - code = 400 - - class BarbicanException(DeckhandException): def __init__(self, message, code): diff --git a/deckhand/tests/unit/base.py b/deckhand/tests/unit/base.py index cebbbe63..300c3ce4 100644 --- a/deckhand/tests/unit/base.py +++ b/deckhand/tests/unit/base.py @@ -45,6 +45,31 @@ class DeckhandTestCase(testtools.TestCase): elif isinstance(collection, dict): self.assertEqual(0, len(collection.keys())) + def assertDictItemsAlmostEqual(self, first, second, ignore): + """Assert that the items in a dictionary or list of dictionaries + are equal, except for the keys specified in ``ignore``. + + Both first and second must contain the keys specified in ``ignore``. + + :param first: First dictionary or list of dictionaries to compare. + :type first: dict or list[dict] + :param second: Second dictionary or list of dictionaries to compare. + :type second: dict or list[dict] + :param ignore: List of keys to ignore in both dictionaries or list + of dictionaries. + :type ignore: list or tuple + """ + if not isinstance(first, list): + first = [first] + if not isinstance(second, list): + second = [second] + for key in ignore: + for item in first: + item.pop(key) + for item in second: + item.pop(key) + self.assertEqual(first, second) + def patch(self, target, autospec=True, **kwargs): """Returns a started `mock.patch` object for the supplied target. diff --git a/deckhand/tests/unit/db/test_revision_rollback.py b/deckhand/tests/unit/db/test_revision_rollback.py index 361a99e3..0c587e56 100644 --- a/deckhand/tests/unit/db/test_revision_rollback.py +++ b/deckhand/tests/unit/db/test_revision_rollback.py @@ -83,3 +83,18 @@ class TestRevisionRollback(base.TestDbBase): [d['revision_id'] for d in rollback_documents]) self.assertEqual([1, 1, 4, 4], [d['orig_revision_id'] for d in rollback_documents]) + + def test_rollback_to_revision_same_as_current_revision(self): + 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'] + orig_documents = self.list_revision_documents(orig_revision_id) + + rollback_revision = self.rollback_revision(orig_revision_id) + self.assertDictItemsAlmostEqual( + sorted(orig_documents, key=lambda d: d['created_at']), + sorted(rollback_revision['documents'], + key=lambda d: d['created_at']), + ignore=['created_at', 'updated_at', 'revision_id', + 'orig_revision_id', 'id']) diff --git a/deckhand/tests/unit/db/test_revision_rollback_negative.py b/deckhand/tests/unit/db/test_revision_rollback_negative.py deleted file mode 100644 index 77f91ff9..00000000 --- a/deckhand/tests/unit/db/test_revision_rollback_negative.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2017 AT&T Intellectual Property. All other rights reserved. -# -# 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 deckhand import errors -from deckhand.tests import test_utils -from deckhand.tests.unit.db import base - - -class TestRevisionRollbackNegative(base.TestDbBase): - - def test_rollback_same_revision_raises_error(self): - # Revision 1: Create 4 documents. - 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'] - - # Attempt to rollback to the latest revision, which should result - # in an error. - self.assertRaises( - errors.InvalidRollback, self.rollback_revision, orig_revision_id) - - def test_rollback_unchanged_revision_history_raises_error(self): - # Revision 1: Create 4 documents. - 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'] - - # Create a 2nd revision that is a carbon-copy of 1st. - self.create_documents(bucket_name, payload) - - # Attempt to rollback to the 1st revision, which should result in an - # error, as it is identical to the latest revision. - self.assertRaises( - errors.InvalidRollback, self.rollback_revision, orig_revision_id)