From a1df0dbb03832385d51594d0da6523ee098e99dc Mon Sep 17 00:00:00 2001 From: Kasper Hasior Date: Wed, 3 Apr 2019 13:09:51 +0200 Subject: [PATCH] Changing file owner when upgrading mariadb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we upgrade mariadb version we mount volumes with data and config files. As we use new images we cannot make assumption that UIDs are the same as on the old image so we set a new files and directories owner. Two new functions have been added: _restore_directory() and _restore_home_directory() in class Manager which are responsible to copy files from the volume to the instance file system and to change owner and group of the files. The new function is located in base Manager class as it very likely that other datastores may use it as well. Co-Authored-By: Przemysław Godek Change-Id: I3c39f51b471081eeabed55070dc91807544a2dda Story: #2005398 Task: #30391 Signed-off-by: Kasper Hasior --- trove/guestagent/common/operating_system.py | 6 ++++ trove/guestagent/datastore/manager.py | 16 +++++++++ .../datastore/mysql_common/manager.py | 17 +++++---- trove/guestagent/datastore/service.py | 3 +- .../unittests/guestagent/test_manager.py | 36 +++++++++++++++++++ 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/trove/guestagent/common/operating_system.py b/trove/guestagent/common/operating_system.py index 89446d1130..6142168f7b 100644 --- a/trove/guestagent/common/operating_system.py +++ b/trove/guestagent/common/operating_system.py @@ -16,6 +16,7 @@ import inspect import operator import os +import pwd import re import stat import tempfile @@ -861,3 +862,8 @@ def is_mount(path): directory_dev = get_device(path, as_root=True) parent_dev = get_device(os.path.join(path, '..'), as_root=True) return directory_dev != parent_dev + + +def get_current_user(): + """Returns name of the current OS user""" + return pwd.getpwuid(os.getuid())[0] diff --git a/trove/guestagent/datastore/manager.py b/trove/guestagent/datastore/manager.py index 3c35b26595..c91d5517e7 100644 --- a/trove/guestagent/datastore/manager.py +++ b/trove/guestagent/datastore/manager.py @@ -16,6 +16,7 @@ import abc import operator +import os from oslo_config import cfg as oslo_cfg from oslo_log import log as logging @@ -409,6 +410,21 @@ class Manager(periodic_task.PeriodicTasks): """ pass + def _restore_directory(self, restore_dir, target_dir, owner=None): + restore_path = os.path.join(restore_dir, ".") + operating_system.copy(restore_path, target_dir, + preserve=True, as_root=True) + if owner is not None: + operating_system.chown(path=target_dir, user=owner, group=owner, + recursive=True, as_root=True) + + def _restore_home_directory(self, saved_home_dir): + home_dir = os.path.expanduser("~") + home_owner = operating_system.get_current_user() + self._restore_directory(restore_dir=saved_home_dir, + target_dir=home_dir, + owner=home_owner) + ################# # Service related ################# diff --git a/trove/guestagent/datastore/mysql_common/manager.py b/trove/guestagent/datastore/mysql_common/manager.py index f26d429980..535fef0339 100644 --- a/trove/guestagent/datastore/mysql_common/manager.py +++ b/trove/guestagent/datastore/mysql_common/manager.py @@ -278,17 +278,20 @@ class MySqlManager(manager.Manager): self.mount_volume(context, mount_point=upgrade_info['mount_point'], device_path=upgrade_info['device'], write_to_fstab=True) + operating_system.chown(path=upgrade_info['mount_point'], + user=service.MYSQL_OWNER, + group=service.MYSQL_OWNER, + recursive=True, as_root=True) + + self._restore_home_directory(upgrade_info['home_save']) if operating_system.exists(upgrade_info['save_etc_dir'], is_directory=True, as_root=True): - operating_system.copy("%s/." % upgrade_info['save_etc_dir'], - "/etc", preserve=True, as_root=True) + self._restore_directory(upgrade_info['save_etc_dir'], "/etc") + + self._restore_directory("%s/." % upgrade_info['save_dir'], + "/etc/mysql") - operating_system.copy("%s/." % upgrade_info['save_dir'], "/etc/mysql", - preserve=True, as_root=True) - operating_system.copy("%s/." % upgrade_info['home_save'], - os.path.expanduser('~'), - preserve=True, as_root=True) self.configuration_manager.refresh_cache() app.start_mysql() app.status.end_restart() diff --git a/trove/guestagent/datastore/service.py b/trove/guestagent/datastore/service.py index aec1e24e22..fb6c698d9a 100644 --- a/trove/guestagent/datastore/service.py +++ b/trove/guestagent/datastore/service.py @@ -81,9 +81,10 @@ class BaseDbStatus(object): # Set the value of __prepared_completed based on the existence of # the file. This is required as the state is cached so this method # must be called any time the existence of the file changes. - self.__prepare_completed = os.path.isfile( + is_file = os.path.isfile( guestagent_utils.build_file_path( self.GUESTAGENT_DIR, self.PREPARE_END_FILENAME)) + self.__prepare_completed = is_file if is_file else None def begin_install(self): """First call of the DB prepare.""" diff --git a/trove/tests/unittests/guestagent/test_manager.py b/trove/tests/unittests/guestagent/test_manager.py index c8bc796e36..938f8e0fcd 100644 --- a/trove/tests/unittests/guestagent/test_manager.py +++ b/trove/tests/unittests/guestagent/test_manager.py @@ -484,6 +484,42 @@ class ManagerTest(trove_testtools.TestCase): error_occurred=True, post_processing=ANY) + @patch.object(operating_system, 'copy') + @patch.object(operating_system, 'chown') + def test_restore_directory_with_owner(self, chown_mock, copy_mock): + restore_dir = '/restore_directory' + restore_files = '/restore_directory/.' + target_dir = '/target_directory' + owner = 'owner' + self.manager._restore_directory(restore_dir, target_dir, owner) + copy_mock.assert_called_once_with(restore_files, target_dir, + preserve=True, as_root=True) + chown_mock.assert_called_once_with(path=target_dir, user=owner, + group=owner, recursive=True, + as_root=True) + + @patch.object(operating_system, 'copy') + @patch.object(operating_system, 'chown') + def test_restore_directory_without_owner(self, chown_mock, copy_mock): + restore_dir = '/restore_directory' + restore_files = '/restore_directory/.' + target_dir = '/target_directory' + self.manager._restore_directory(restore_dir, target_dir) + copy_mock.assert_called_once_with(restore_files, target_dir, + preserve=True, as_root=True) + chown_mock.assert_not_called() + + @patch.object(manager.Manager, '_restore_directory') + @patch.object(operating_system, 'get_current_user', return_value='trove') + def test_restore_home_directory(self, os_mock, restore_mock): + saved_home_dir = '/old_home' + with patch.object(os.path, 'expanduser', return_value='/home/trove'): + self.manager._restore_home_directory(saved_home_dir) + os_mock.assert_any_call() + restore_mock.assert_called_once_with(restore_dir=saved_home_dir, + target_dir='/home/trove', + owner='trove') + def test_module_list(self): with patch.object(module_manager.ModuleManager, 'read_module_results', return_value=[