From 59a972b312c8d25ffb46b4a994374490731747ea Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 5 Nov 2020 09:44:57 +1300 Subject: [PATCH] Fix upgrading instance datastore version Change-Id: I0456688c2e6c72b0312f1f79bb3e68f13fd2cb6e --- .../datastore/mysql_common/service.py | 5 ++ trove/guestagent/utils/docker.py | 8 ++++ trove/instance/service.py | 9 ++++ .../tests/unittests/instance/test_service.py | 48 +++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/trove/guestagent/datastore/mysql_common/service.py b/trove/guestagent/datastore/mysql_common/service.py index d3d44bbc83..50e5ebe7b0 100644 --- a/trove/guestagent/datastore/mysql_common/service.py +++ b/trove/guestagent/datastore/mysql_common/service.py @@ -801,6 +801,8 @@ class BaseMySqlApp(service.BaseDbApp): def upgrade(self, upgrade_info): """Upgrade the database.""" new_version = upgrade_info.get('datastore_version') + if new_version == CONF.datastore_version: + return LOG.info('Stopping db container for upgrade') self.stop_db() @@ -808,6 +810,9 @@ class BaseMySqlApp(service.BaseDbApp): LOG.info('Deleting db container for upgrade') docker_util.remove_container(self.docker_client) + LOG.info('Remove unused images before starting new db container') + docker_util.prune_images(self.docker_client) + LOG.info('Starting new db container with version %s for upgrade', new_version) self.start_db(update_db=True, ds_version=new_version) diff --git a/trove/guestagent/utils/docker.py b/trove/guestagent/utils/docker.py index da3ad41215..0fdda04980 100644 --- a/trove/guestagent/utils/docker.py +++ b/trove/guestagent/utils/docker.py @@ -151,3 +151,11 @@ def get_container_logs(client, name='database', tail=50): container = client.containers.get(name) output = container.logs(tail=tail) return _decode_output(output) + + +def prune_images(client): + """Remove unused images.""" + try: + client.images.prune(filters={'dangling': False}) + except Exception as e: + LOG.warning(f"Prune image failed, error: {str(e)}") diff --git a/trove/instance/service.py b/trove/instance/service.py index 3884f9e34c..86da146b0b 100644 --- a/trove/instance/service.py +++ b/trove/instance/service.py @@ -528,6 +528,12 @@ class InstanceController(wsgi.Controller): elif 'datastore_version' in kwargs: datastore_version = ds_models.DatastoreVersion.load( instance.datastore, kwargs['datastore_version']) + + if datastore_version.name == instance.ds_version.name: + LOG.warning(f"Same datastore version {datastore_version.name} " + f"for upgrading") + return + context.notification = ( notification.DBaaSInstanceUpgrade(context, request=req)) with StartNotification(context, instance_id=instance.id, @@ -575,6 +581,9 @@ class InstanceController(wsgi.Controller): if 'access' in body['instance']: args['access'] = body['instance']['access'] + if 'datastore_version' in body['instance']: + args['datastore_version'] = body['instance']['datastore_version'] + self._modify_instance(context, req, instance, **args) return wsgi.Result(None, 202) diff --git a/trove/tests/unittests/instance/test_service.py b/trove/tests/unittests/instance/test_service.py index e263565498..07444a4c6c 100644 --- a/trove/tests/unittests/instance/test_service.py +++ b/trove/tests/unittests/instance/test_service.py @@ -15,7 +15,9 @@ from unittest import mock from trove.common import clients from trove.datastore import models as ds_models +from trove.instance import models as ins_models from trove.instance import service +from trove.instance import service_status as srvstatus from trove.tests.unittests import trove_testtools from trove.tests.unittests.util import util @@ -51,6 +53,10 @@ class TestInstanceController(trove_testtools.TestCase): super(TestInstanceController, cls).tearDownClass() + def setUp(self): + trove_testtools.patch_notifier(self) + super(TestInstanceController, self).setUp() + @mock.patch.object(clients, 'create_glance_client') @mock.patch('trove.instance.models.Instance.create') def test_create_by_ds_version_image_tags(self, mock_model_create, @@ -78,3 +84,45 @@ class TestInstanceController(trove_testtools.TestCase): filters={'tag': ['trove', 'mysql'], 'status': 'active'}, sort='created_at:desc', limit=1 ) + + @mock.patch.object(clients, 'create_nova_client', + return_value=mock.MagicMock()) + @mock.patch('trove.rpc.get_client') + def test_update_datastore_version(self, mock_get_rpc_client, + mock_create_nova_client): + # Create an instance in db. + instance = ins_models.DBInstance.create( + name=self.random_name('instance'), + flavor_id=self.random_uuid(), + tenant_id=self.random_uuid(), + volume_size=1, + datastore_version_id=self.ds_version_imageid.id, + task_status=ins_models.InstanceTasks.BUILDING, + compute_instance_id=self.random_uuid() + ) + ins_models.InstanceServiceStatus.create( + instance_id=instance.id, + status=srvstatus.ServiceStatuses.NEW + ) + + # Create a new datastore version in db. + new_version_name = self.random_name('version') + ds_models.update_datastore_version( + self.ds_name, new_version_name, + 'mysql', self.random_uuid(), [], '', 1 + ) + new_ds_version = ds_models.DatastoreVersion.load( + self.ds, new_version_name) + + body = { + 'instance': { + 'datastore_version': new_ds_version.id + } + } + self.controller.update(mock.MagicMock(), instance.id, body, mock.ANY) + + rpc_ctx = mock_get_rpc_client.return_value.prepare.return_value + rpc_ctx.cast.assert_called_once_with( + mock.ANY, "upgrade", + instance_id=instance.id, + datastore_version_id=new_ds_version.id)