summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api-ref/source/backups.inc2
-rw-r--r--releasenotes/notes/victoria-list-project-backups.yaml3
-rw-r--r--trove/backup/models.py7
-rw-r--r--trove/backup/service.py4
-rw-r--r--trove/tests/unittests/backup/test_backup_controller.py66
5 files changed, 78 insertions, 4 deletions
diff --git a/api-ref/source/backups.inc b/api-ref/source/backups.inc
index 8b52f230..61828ef9 100644
--- a/api-ref/source/backups.inc
+++ b/api-ref/source/backups.inc
@@ -20,6 +20,8 @@ using query string parameters. The following filters are supported:
- ``all_projects=True/False`` - Return the list of backups for all the
projects, this is an admin only param by default.
- ``datastore={datastore}`` - Return a list of backups of the same datastore.
+- ``project_id={project_id}`` - Get backups of a specific project. Admin
+ required.
Normal response codes: 200
diff --git a/releasenotes/notes/victoria-list-project-backups.yaml b/releasenotes/notes/victoria-list-project-backups.yaml
new file mode 100644
index 00000000..b45638b9
--- /dev/null
+++ b/releasenotes/notes/victoria-list-project-backups.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - The admin user is able to get backups of a specific project.
diff --git a/trove/backup/models.py b/trove/backup/models.py
index 95d8683f..10c58ad2 100644
--- a/trove/backup/models.py
+++ b/trove/backup/models.py
@@ -186,13 +186,16 @@ class Backup(object):
return query.all(), marker
@classmethod
- def list(cls, context, datastore=None, instance_id=None,
+ def list(cls, context, datastore=None, instance_id=None, project_id=None,
all_projects=False):
query = DBBackup.query()
filters = [DBBackup.deleted == 0]
- if not all_projects:
+ if project_id:
+ filters.append(DBBackup.tenant_id == project_id)
+ elif not all_projects:
filters.append(DBBackup.tenant_id == context.project_id)
+
if instance_id:
filters.append(DBBackup.instance_id == instance_id)
diff --git a/trove/backup/service.py b/trove/backup/service.py
index 095ee9c9..ed783199 100644
--- a/trove/backup/service.py
+++ b/trove/backup/service.py
@@ -44,10 +44,11 @@ class BackupController(wsgi.Controller):
LOG.debug("Listing backups for tenant %s", tenant_id)
datastore = req.GET.get('datastore')
instance_id = req.GET.get('instance_id')
+ project_id = req.GET.get('project_id')
all_projects = strutils.bool_from_string(req.GET.get('all_projects'))
context = req.environ[wsgi.CONTEXT_KEY]
- if all_projects:
+ if project_id or all_projects:
policy.authorize_on_tenant(context, 'backup:index:all_projects')
else:
policy.authorize_on_tenant(context, 'backup:index')
@@ -56,6 +57,7 @@ class BackupController(wsgi.Controller):
context,
datastore=datastore,
instance_id=instance_id,
+ project_id=project_id,
all_projects=all_projects
)
view = views.BackupViews(backups)
diff --git a/trove/tests/unittests/backup/test_backup_controller.py b/trove/tests/unittests/backup/test_backup_controller.py
index d1f73a4b..cf6a8802 100644
--- a/trove/tests/unittests/backup/test_backup_controller.py
+++ b/trove/tests/unittests/backup/test_backup_controller.py
@@ -13,20 +13,36 @@
# License for the specific language governing permissions and limitations
# under the License.
#
+from unittest import mock
+import uuid
+
import jsonschema
from testtools.matchers import Equals
+
+from trove.backup import models
+from trove.backup import state
from trove.backup.service import BackupController
from trove.common import apischema
+from trove.common import context
+from trove.common import wsgi
from trove.tests.unittests import trove_testtools
+from trove.tests.unittests.util import util
class TestBackupController(trove_testtools.TestCase):
-
def setUp(self):
super(TestBackupController, self).setUp()
self.uuid = "d6338c9c-3cc8-4313-b98f-13cc0684cf15"
self.invalid_uuid = "ead-edsa-e23-sdf-23"
self.controller = BackupController()
+ self.context = context.TroveContext(project_id=str(uuid.uuid4()))
+ util.init_db()
+
+ def tearDown(self):
+ super(TestBackupController, self).tearDown()
+ backups = models.DBBackup.find_all(tenant_id=self.context.project_id)
+ for backup in backups:
+ backup.delete()
def test_validate_create_complete(self):
body = {"backup": {"instance": self.uuid,
@@ -87,3 +103,51 @@ class TestBackupController(trove_testtools.TestCase):
self.assertThat(errors[0].message,
Equals("'%s' does not match '%s'" %
(self.invalid_uuid, apischema.uuid['pattern'])))
+
+ def test_list_by_project(self):
+ req = mock.MagicMock(GET={'project_id': self.context.project_id},
+ environ={wsgi.CONTEXT_KEY: self.context},
+ url='http://localhost')
+ instance_id = str(uuid.uuid4())
+ backup_name = str(uuid.uuid4())
+ location = 'https://object-storage.com/tenant/database_backups/backup'
+ models.DBBackup.create(tenant_id=self.context.project_id,
+ name=backup_name,
+ state=state.BackupState.NEW,
+ instance_id=instance_id,
+ deleted=False,
+ size=2.0,
+ location=location)
+
+ res = self.controller.index(req, 'fake_tenant_id')
+
+ self.assertEqual(200, res.status)
+ backups = res.data(None)['backups']
+ self.assertGreaterEqual(len(backups), 1)
+ our_backup = None
+ for backup in backups:
+ if backup['name'] == backup_name:
+ our_backup = backup
+ break
+ self.assertIsNotNone(our_backup)
+ expected = {
+ 'name': backup_name,
+ 'locationRef': location,
+ 'instance_id': instance_id,
+ 'size': 2.0,
+ 'status': 'NEW',
+ }
+ self.assertTrue(
+ set(expected.items()).issubset(set(our_backup.items()))
+ )
+
+ # Get backups of unknown project
+ req = mock.MagicMock(GET={'project_id': str(uuid.uuid4())},
+ environ={wsgi.CONTEXT_KEY: self.context},
+ url='http://localhost')
+
+ res = self.controller.index(req, 'fake_tenant_id')
+
+ self.assertEqual(200, res.status)
+ backups = res.data(None)['backups']
+ self.assertEqual(0, len(backups))