From 1a80368bd25099ab81b8d992d3df546f655f65b4 Mon Sep 17 00:00:00 2001 From: Marcin Skiba Date: Tue, 28 Jun 2016 17:32:03 +0200 Subject: [PATCH 1/6] [LIB-757] - add possibility to restore backups --- syncano/models/backups.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/syncano/models/backups.py b/syncano/models/backups.py index 0d06016..822be92 100644 --- a/syncano/models/backups.py +++ b/syncano/models/backups.py @@ -45,5 +45,21 @@ class Meta: 'list': { 'methods': ['post', 'get'], 'path': '/backups/full/', + }, + 'restore': { + 'methods': ['post'], + 'path': '/restores/', } } + + def restore(self): + properties = self.get_endpoint_data() + endpoint = self._meta.resolve_endpoint('restore', properties) + kwargs = { + 'data': { + 'backup': self.id + } + } + connection = self._get_connection() + connection.request('POST', endpoint, **kwargs) + From cf361bab3564abdac63e847447f6384cedba6efb Mon Sep 17 00:00:00 2001 From: Marcin Skiba Date: Tue, 28 Jun 2016 17:32:37 +0200 Subject: [PATCH 2/6] [LIB-757] - remove obsolete blank lines --- syncano/models/backups.py | 1 - 1 file changed, 1 deletion(-) diff --git a/syncano/models/backups.py b/syncano/models/backups.py index 822be92..f4536e1 100644 --- a/syncano/models/backups.py +++ b/syncano/models/backups.py @@ -62,4 +62,3 @@ def restore(self): } connection = self._get_connection() connection.request('POST', endpoint, **kwargs) - From 3a1d0b622f6bcb010d9846d98625d9aeb26bc118 Mon Sep 17 00:00:00 2001 From: Marcin Skiba Date: Tue, 28 Jun 2016 18:17:03 +0200 Subject: [PATCH 3/6] [LIB-757] - test --- tests/integration_test_backups.py | 34 ++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/integration_test_backups.py b/tests/integration_test_backups.py index 357db55..6661816 100644 --- a/tests/integration_test_backups.py +++ b/tests/integration_test_backups.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- -from syncano.models import Backup +import time + +from syncano.models import Backup, Class, Instance from tests.integration_test import InstanceMixin, IntegrationTest @@ -27,6 +29,35 @@ def _test_backup_list(self): backups = [backup for backup in Backup.please.list()] self.assertTrue(len(backups)) # at least one backup here; + def _test_backup_restore(self, backup_id): + backup = Backup.please.get(id=backup_id) + instance_name = backup.instance + instance = Instance.please.get(name=instance_name) + classes_count = len(list(instance.classes)) + + test_class = Class(name='testclass') + test_class.save() + instance.reload() + classes_count_after_update = len(list(instance.classes)) + + self.assertTrue( + classes_count_after_update - classes_count == 1, + 'There should be only 1 more instance class after new class creation.' + ) + + # wait for backup to be restored + backup.restore() + time.sleep(10) + + instance.reload() + classes_count_after_restore = len(list(instance.classes)) + + self.assertEqual( + classes_count, + classes_count_after_restore, + 'Classes count after restore should be equal to original classes count.' + ) + def _test_backup_delete(self, backup_id): backup = Backup.please.get(id=backup_id) backup.delete() @@ -38,4 +69,5 @@ def test_backup(self): backup_id = self._test_backup_create() self._test_backup_list() self._test_backup_detail(backup_id=backup_id) + self._test_backup_restore(backup_id=backup_id) self._test_backup_delete(backup_id=backup_id) From 5d8a1cec2d2274b15c1f25f325c28a05eea6b1ce Mon Sep 17 00:00:00 2001 From: Marcin Skiba Date: Wed, 29 Jun 2016 07:53:50 +0200 Subject: [PATCH 4/6] [LIB-757] - improve test --- tests/integration_test_backups.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration_test_backups.py b/tests/integration_test_backups.py index 6661816..8fb60a7 100644 --- a/tests/integration_test_backups.py +++ b/tests/integration_test_backups.py @@ -45,9 +45,12 @@ def _test_backup_restore(self, backup_id): 'There should be only 1 more instance class after new class creation.' ) - # wait for backup to be restored + # wait for backup to be truly saved and restored + while backup.status != 'success': + time.sleep(1) + backup.reload() backup.restore() - time.sleep(10) + time.sleep(15) instance.reload() classes_count_after_restore = len(list(instance.classes)) From 4a62a9a45b366e85bc47b44ee7d3e4e90710e2ef Mon Sep 17 00:00:00 2001 From: Marcin Skiba Date: Wed, 29 Jun 2016 08:23:05 +0200 Subject: [PATCH 5/6] [LIB-757] - make backup restore synchronous --- syncano/models/backups.py | 9 ++++++++- tests/integration_test_backups.py | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/syncano/models/backups.py b/syncano/models/backups.py index f4536e1..5d2c50c 100644 --- a/syncano/models/backups.py +++ b/syncano/models/backups.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import time + from . import fields from .base import Model from .instances import Instance @@ -61,4 +63,9 @@ def restore(self): } } connection = self._get_connection() - connection.request('POST', endpoint, **kwargs) + response = connection.request('POST', endpoint, **kwargs) + restore_endpoint = response['links']['self'] + restore_response = connection.request('GET', restore_endpoint) + while restore_response['status'] == 'running': + time.sleep(1) + restore_response = connection.request('GET', restore_endpoint) diff --git a/tests/integration_test_backups.py b/tests/integration_test_backups.py index 8fb60a7..c6de7f0 100644 --- a/tests/integration_test_backups.py +++ b/tests/integration_test_backups.py @@ -45,12 +45,12 @@ def _test_backup_restore(self, backup_id): 'There should be only 1 more instance class after new class creation.' ) - # wait for backup to be truly saved and restored + # wait for backup to be saved while backup.status != 'success': time.sleep(1) backup.reload() + backup.restore() - time.sleep(15) instance.reload() classes_count_after_restore = len(list(instance.classes)) From d84be9de5ade6a8a54c2e46e1f76be4fe3f8756c Mon Sep 17 00:00:00 2001 From: Marcin Skiba Date: Thu, 30 Jun 2016 16:35:05 +0200 Subject: [PATCH 6/6] [LIB-757] - Remove unused imports. --- syncano/models/backups.py | 45 ++++++++++++++++++------------- tests/integration_test_backups.py | 36 +++++++------------------ 2 files changed, 35 insertions(+), 46 deletions(-) diff --git a/syncano/models/backups.py b/syncano/models/backups.py index 5d2c50c..bf77353 100644 --- a/syncano/models/backups.py +++ b/syncano/models/backups.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -import time - from . import fields from .base import Model from .instances import Instance @@ -48,24 +46,33 @@ class Meta: 'methods': ['post', 'get'], 'path': '/backups/full/', }, - 'restore': { - 'methods': ['post'], - 'path': '/restores/', - } } - def restore(self): - properties = self.get_endpoint_data() - endpoint = self._meta.resolve_endpoint('restore', properties) - kwargs = { - 'data': { - 'backup': self.id + def schedule_restore(self): + restore = Restore(backup=self.id).save() + return restore + + +class Restore(Model): + + author = fields.ModelField('Admin') + status = fields.StringField(read_only=True) + status_info = fields.StringField(read_only=True) + updated_at = fields.DateTimeField(read_only=True, required=False) + created_at = fields.DateTimeField(read_only=True, required=False) + links = fields.LinksField() + backup = fields.StringField() + archive = fields.StringField(read_only=True) + + class Meta: + parent = Instance + endpoints = { + 'list': { + 'methods': ['get', 'post'], + 'path': '/restores/', + }, + 'detail': { + 'methods': ['get'], + 'path': '/restores/{id}/', } } - connection = self._get_connection() - response = connection.request('POST', endpoint, **kwargs) - restore_endpoint = response['links']['self'] - restore_response = connection.request('GET', restore_endpoint) - while restore_response['status'] == 'running': - time.sleep(1) - restore_response = connection.request('GET', restore_endpoint) diff --git a/tests/integration_test_backups.py b/tests/integration_test_backups.py index c6de7f0..b78f870 100644 --- a/tests/integration_test_backups.py +++ b/tests/integration_test_backups.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import time -from syncano.models import Backup, Class, Instance +from syncano.models import Backup from tests.integration_test import InstanceMixin, IntegrationTest @@ -29,37 +29,19 @@ def _test_backup_list(self): backups = [backup for backup in Backup.please.list()] self.assertTrue(len(backups)) # at least one backup here; - def _test_backup_restore(self, backup_id): + def _test_backup_schedule_restore(self, backup_id): backup = Backup.please.get(id=backup_id) - instance_name = backup.instance - instance = Instance.please.get(name=instance_name) - classes_count = len(list(instance.classes)) - - test_class = Class(name='testclass') - test_class.save() - instance.reload() - classes_count_after_update = len(list(instance.classes)) - - self.assertTrue( - classes_count_after_update - classes_count == 1, - 'There should be only 1 more instance class after new class creation.' - ) # wait for backup to be saved - while backup.status != 'success': + seconds_waited = 0 + while backup.status in ['scheduled', 'running']: + seconds_waited += 1 + self.assertTrue(seconds_waited < 20, 'Waiting for backup to be saved takes too long.') time.sleep(1) backup.reload() - backup.restore() - - instance.reload() - classes_count_after_restore = len(list(instance.classes)) - - self.assertEqual( - classes_count, - classes_count_after_restore, - 'Classes count after restore should be equal to original classes count.' - ) + restore = backup.schedule_restore() + self.assertIn(restore.status, ['success', 'scheduled']) def _test_backup_delete(self, backup_id): backup = Backup.please.get(id=backup_id) @@ -72,5 +54,5 @@ def test_backup(self): backup_id = self._test_backup_create() self._test_backup_list() self._test_backup_detail(backup_id=backup_id) - self._test_backup_restore(backup_id=backup_id) + self._test_backup_schedule_restore(backup_id=backup_id) self._test_backup_delete(backup_id=backup_id)