Merge "Support getting backups of a specific project"
This commit is contained in:
commit
2cd5bb6f83
|
@ -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
|
- ``all_projects=True/False`` - Return the list of backups for all the
|
||||||
projects, this is an admin only param by default.
|
projects, this is an admin only param by default.
|
||||||
- ``datastore={datastore}`` - Return a list of backups of the same datastore.
|
- ``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
|
Normal response codes: 200
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- The admin user is able to get backups of a specific project.
|
|
@ -186,13 +186,16 @@ class Backup(object):
|
||||||
return query.all(), marker
|
return query.all(), marker
|
||||||
|
|
||||||
@classmethod
|
@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):
|
all_projects=False):
|
||||||
query = DBBackup.query()
|
query = DBBackup.query()
|
||||||
filters = [DBBackup.deleted == 0]
|
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)
|
filters.append(DBBackup.tenant_id == context.project_id)
|
||||||
|
|
||||||
if instance_id:
|
if instance_id:
|
||||||
filters.append(DBBackup.instance_id == instance_id)
|
filters.append(DBBackup.instance_id == instance_id)
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,11 @@ class BackupController(wsgi.Controller):
|
||||||
LOG.debug("Listing backups for tenant %s", tenant_id)
|
LOG.debug("Listing backups for tenant %s", tenant_id)
|
||||||
datastore = req.GET.get('datastore')
|
datastore = req.GET.get('datastore')
|
||||||
instance_id = req.GET.get('instance_id')
|
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'))
|
all_projects = strutils.bool_from_string(req.GET.get('all_projects'))
|
||||||
context = req.environ[wsgi.CONTEXT_KEY]
|
context = req.environ[wsgi.CONTEXT_KEY]
|
||||||
|
|
||||||
if all_projects:
|
if project_id or all_projects:
|
||||||
policy.authorize_on_tenant(context, 'backup:index:all_projects')
|
policy.authorize_on_tenant(context, 'backup:index:all_projects')
|
||||||
else:
|
else:
|
||||||
policy.authorize_on_tenant(context, 'backup:index')
|
policy.authorize_on_tenant(context, 'backup:index')
|
||||||
|
@ -56,6 +57,7 @@ class BackupController(wsgi.Controller):
|
||||||
context,
|
context,
|
||||||
datastore=datastore,
|
datastore=datastore,
|
||||||
instance_id=instance_id,
|
instance_id=instance_id,
|
||||||
|
project_id=project_id,
|
||||||
all_projects=all_projects
|
all_projects=all_projects
|
||||||
)
|
)
|
||||||
view = views.BackupViews(backups)
|
view = views.BackupViews(backups)
|
||||||
|
|
|
@ -13,20 +13,36 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
from unittest import mock
|
||||||
|
import uuid
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
from testtools.matchers import Equals
|
from testtools.matchers import Equals
|
||||||
|
|
||||||
|
from trove.backup import models
|
||||||
|
from trove.backup import state
|
||||||
from trove.backup.service import BackupController
|
from trove.backup.service import BackupController
|
||||||
from trove.common import apischema
|
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 import trove_testtools
|
||||||
|
from trove.tests.unittests.util import util
|
||||||
|
|
||||||
|
|
||||||
class TestBackupController(trove_testtools.TestCase):
|
class TestBackupController(trove_testtools.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestBackupController, self).setUp()
|
super(TestBackupController, self).setUp()
|
||||||
self.uuid = "d6338c9c-3cc8-4313-b98f-13cc0684cf15"
|
self.uuid = "d6338c9c-3cc8-4313-b98f-13cc0684cf15"
|
||||||
self.invalid_uuid = "ead-edsa-e23-sdf-23"
|
self.invalid_uuid = "ead-edsa-e23-sdf-23"
|
||||||
self.controller = BackupController()
|
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):
|
def test_validate_create_complete(self):
|
||||||
body = {"backup": {"instance": self.uuid,
|
body = {"backup": {"instance": self.uuid,
|
||||||
|
@ -87,3 +103,51 @@ class TestBackupController(trove_testtools.TestCase):
|
||||||
self.assertThat(errors[0].message,
|
self.assertThat(errors[0].message,
|
||||||
Equals("'%s' does not match '%s'" %
|
Equals("'%s' does not match '%s'" %
|
||||||
(self.invalid_uuid, apischema.uuid['pattern'])))
|
(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))
|
||||||
|
|
Loading…
Reference in New Issue