From f0fca1ee85cd524369e9e2a5968371260e7d2d56 Mon Sep 17 00:00:00 2001 From: Michael Basnight Date: Mon, 24 Jun 2013 08:45:41 -0700 Subject: [PATCH] Create templated config files Removes the need for apt package installation for configuration files. implements blueprint guestagent-mycnf-versioning Change-Id: I2f3c5376179c6677ef1e812b3d1be3e03cde39c0 --- requirements.txt | 1 + trove/common/template.py | 46 ++++++++++++++ trove/guestagent/api.py | 10 +-- trove/guestagent/manager/mysql.py | 12 ++-- trove/guestagent/manager/mysql_service.py | 61 +++++++------------ trove/taskmanager/models.py | 43 ++++++++++--- trove/templates/mysql.config.template | 58 ++++++++++++++++++ trove/tests/api/instances_resize.py | 7 ++- trove/tests/fakes/guestagent.py | 5 +- trove/tests/unittests/common/test_template.py | 57 +++++++++++++++++ trove/tests/unittests/guestagent/test_api.py | 14 +++-- .../tests/unittests/guestagent/test_dbaas.py | 46 ++++---------- .../unittests/guestagent/test_manager.py | 3 +- 13 files changed, 262 insertions(+), 101 deletions(-) create mode 100644 trove/common/template.py create mode 100644 trove/templates/mysql.config.template create mode 100644 trove/tests/unittests/common/test_template.py diff --git a/requirements.txt b/requirements.txt index 666e20c87f..f58adc9626 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,4 @@ python-swiftclient iso8601 oslo.config>=1.1.0 jsonschema>=1.0.0,!=1.4.0,<2 +Jinja2 diff --git a/trove/common/template.py b/trove/common/template.py new file mode 100644 index 0000000000..dafc626be2 --- /dev/null +++ b/trove/common/template.py @@ -0,0 +1,46 @@ +# Copyright 2012 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +_ENV = None + +import jinja2 + + +def get_env(): + global _ENV + if not _ENV: + _ENV = create_env() + return _ENV + + +def create_env(): + loader = jinja2.ChoiceLoader([ + jinja2.FileSystemLoader("/etc/trove/templates"), + jinja2.PackageLoader("trove", "templates") + ]) + return jinja2.Environment(loader=loader) + + +class SingleInstanceConfigTemplate(object): + _location_types = {'mysql': '/etc/mysql/my.cnf', + 'percona': '/etc/mysql/my.cnf'} + + def __init__(self, location_type, flavor_dict): + self.config_location = self._location_types[location_type] + self.flavor_dict = flavor_dict + self.template = get_env().get_template("mysql.config.template") + + def render(self): + self.config_contents = self.template.render( + flavor=self.flavor_dict) diff --git a/trove/guestagent/api.py b/trove/guestagent/api.py index cf9ba72a8d..b768e54e25 100644 --- a/trove/guestagent/api.py +++ b/trove/guestagent/api.py @@ -205,7 +205,7 @@ class API(proxy.RpcProxy): def prepare(self, memory_mb, databases, users, device_path='/dev/vdb', mount_point='/mnt/volume', - backup_id=None): + backup_id=None, config_location=None, config_contents=None): """Make an asynchronous call to prepare the guest as a database container optionally includes a backup id for restores """ @@ -213,19 +213,21 @@ class API(proxy.RpcProxy): self._cast_with_consumer( "prepare", databases=databases, memory_mb=memory_mb, users=users, device_path=device_path, mount_point=mount_point, - backup_id=backup_id) + backup_id=backup_id, config_location=config_location, + config_contents=config_contents) def restart(self): """Restart the MySQL server.""" LOG.debug(_("Sending the call to restart MySQL on the Guest.")) self._call("restart", AGENT_HIGH_TIMEOUT) - def start_db_with_conf_changes(self, updated_memory_size): + def start_db_with_conf_changes(self, config_location, config_contents): """Start the MySQL server.""" LOG.debug(_("Sending the call to start MySQL on the Guest with " "a timeout of %s.") % AGENT_HIGH_TIMEOUT) self._call("start_db_with_conf_changes", AGENT_HIGH_TIMEOUT, - updated_memory_size=updated_memory_size) + config_location=config_location, + config_contents=config_contents) def reset_configuration(self, configuration): """Ignore running state of MySQL, and just change the config file diff --git a/trove/guestagent/manager/mysql.py b/trove/guestagent/manager/mysql.py index eb24dfd80f..bc277274fb 100644 --- a/trove/guestagent/manager/mysql.py +++ b/trove/guestagent/manager/mysql.py @@ -18,7 +18,7 @@ CONF = cfg.CONF class Manager(periodic_task.PeriodicTasks): - @periodic_task.periodic_task(ticks_between_runs=10) + @periodic_task.periodic_task(ticks_between_runs=3) def update_status(self, context): """Update the status of the MySQL service""" MySqlAppStatus.get().update() @@ -82,7 +82,8 @@ class Manager(periodic_task.PeriodicTasks): LOG.info(_("Restored database successfully")) def prepare(self, context, databases, memory_mb, users, device_path=None, - mount_point=None, backup_id=None): + mount_point=None, backup_id=None, config_location=None, + config_contents=None): """Makes ready DBAAS on a Guest container.""" MySqlAppStatus.get().begin_mysql_install() # status end_mysql_install set with secure() @@ -109,7 +110,7 @@ class Manager(periodic_task.PeriodicTasks): if backup_id: self._perform_restore(backup_id, context, CONF.mount_point, app) LOG.info(_("Securing mysql now.")) - app.secure(memory_mb) + app.secure(config_location, config_contents) if backup_id and MySqlAdmin().is_root_enabled(): MySqlAdmin().report_root_enabled(context) else: @@ -128,9 +129,10 @@ class Manager(periodic_task.PeriodicTasks): app = MySqlApp(MySqlAppStatus.get()) app.restart() - def start_db_with_conf_changes(self, context, updated_memory_size): + def start_db_with_conf_changes(self, context, config_location, + config_contents): app = MySqlApp(MySqlAppStatus.get()) - app.start_db_with_conf_changes(updated_memory_size) + app.start_db_with_conf_changes(config_location, config_contents) def stop_db(self, context, do_not_start_on_reboot=False): app = MySqlApp(MySqlAppStatus.get()) diff --git a/trove/guestagent/manager/mysql_service.py b/trove/guestagent/manager/mysql_service.py index 2337100583..57fcccf6e5 100644 --- a/trove/guestagent/manager/mysql_service.py +++ b/trove/guestagent/manager/mysql_service.py @@ -29,10 +29,7 @@ MYSQLD_ARGS = None PREPARING = False UUID = False -ORIG_MYCNF = "/etc/mysql/my.cnf" -FINAL_MYCNF = "/var/lib/mysql/my.cnf" TMP_MYCNF = "/tmp/my.cnf.tmp" -DBAAS_MYCNF = "/etc/dbaas/my.cnf/my.cnf.%dM" MYSQL_BASE_DIR = "/var/lib/mysql" CONF = cfg.CONF @@ -614,7 +611,7 @@ class MySqlApp(object): def complete_install_or_restart(self): self.status.end_install_or_restart() - def secure(self, memory_mb): + def secure(self, config_location, config_contents): LOG.info(_("Generating admin password...")) admin_password = generate_random_password() @@ -625,7 +622,7 @@ class MySqlApp(object): self._create_admin_user(client, admin_password) self.stop_db() - self._write_mycnf(memory_mb, admin_password) + self._write_mycnf(admin_password, config_location, config_contents) self.start_mysql() LOG.info(_("Dbaas secure complete.")) @@ -757,40 +754,27 @@ class MySqlApp(object): if "No such file or directory" not in str(pe): raise - def _write_mycnf(self, update_memory_mb, admin_password): + def _write_mycnf(self, admin_password, config_location, config_contents): """ - Install the set of mysql my.cnf templates from dbaas-mycnf package. - The package generates a template suited for the current - container flavor. Update the os_admin user and password - to the my.cnf file for direct login from localhost + Install the set of mysql my.cnf templates. + Update the os_admin user and password to the my.cnf + file for direct login from localhost """ LOG.info(_("Writing my.cnf templates.")) if admin_password is None: admin_password = get_auth_password() - # As of right here, the admin_password contains the password to be - # applied to the my.cnf file, whether it was there before (and we - # passed it in) or we generated a new one just now (because we didn't - # find it). + with open(TMP_MYCNF, 'w') as t: + t.write(config_contents) + utils.execute_with_timeout("sudo", "mv", TMP_MYCNF, + config_location) - LOG.debug(_("Installing my.cnf templates")) - pkg.pkg_install("dbaas-mycnf", self.TIME_OUT) - - LOG.info(_("Replacing my.cnf with template.")) - template_path = DBAAS_MYCNF % update_memory_mb - # replace my.cnf with template. - self._replace_mycnf_with_template(template_path, ORIG_MYCNF) - - LOG.info(_("Writing new temp my.cnf.")) - self._write_temp_mycnf_with_admin_account(ORIG_MYCNF, TMP_MYCNF, + self._write_temp_mycnf_with_admin_account(config_location, + TMP_MYCNF, admin_password) - # permissions work-around - LOG.info(_("Moving tmp into final.")) - utils.execute_with_timeout("sudo", "mv", TMP_MYCNF, FINAL_MYCNF) - LOG.info(_("Removing original my.cnf.")) - utils.execute_with_timeout("sudo", "rm", ORIG_MYCNF) - LOG.info(_("Symlinking final my.cnf.")) - utils.execute_with_timeout("sudo", "ln", "-s", FINAL_MYCNF, ORIG_MYCNF) + utils.execute_with_timeout("sudo", "mv", TMP_MYCNF, + config_location) + self.wipe_ib_logfiles() def start_mysql(self, update_db=False): @@ -825,9 +809,8 @@ class MySqlApp(object): self.status.end_install_or_restart() raise RuntimeError("Could not start MySQL!") - def start_db_with_conf_changes(self, updated_memory_mb): - LOG.info(_("Starting mysql with conf changes to memory(%s)...") - % updated_memory_mb) + def start_db_with_conf_changes(self, config_location, config_contents): + LOG.info(_("Starting mysql with conf changes...")) LOG.info(_("inside the guest - self.status.is_mysql_running(%s)...") % self.status.is_mysql_running) if self.status.is_mysql_running: @@ -835,14 +818,14 @@ class MySqlApp(object): "MySQL state == %s!") % self.status) raise RuntimeError("MySQL not stopped.") LOG.info(_("Initiating config.")) - self._write_mycnf(updated_memory_mb, None) + self._write_mycnf(None, config_location, config_contents) self.start_mysql(True) def reset_configuration(self, configuration): - updated_memory_mb = configuration['memory_mb'] - LOG.info(_("Changing configuration to memory(%s)...") - % updated_memory_mb) - self._write_mycnf(updated_memory_mb, None) + config_location = configuration['config_location'] + config_contents = configuration['config_contents'] + LOG.info(_("Resetting configuration")) + self._write_mycnf(None, config_location, config_contents) def is_installed(self): #(cp16net) could raise an exception, does it need to be handled here? diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py index f5bfb8058c..73d969a402 100644 --- a/trove/taskmanager/models.py +++ b/trove/taskmanager/models.py @@ -17,6 +17,7 @@ import traceback from eventlet import greenthread from novaclient import exceptions as nova_exceptions from trove.common import cfg +from trove.common import template from trove.common import utils from trove.common.exception import GuestError from trove.common.exception import GuestTimeout @@ -110,7 +111,20 @@ class NotifyMixin(object): payload) -class FreshInstanceTasks(FreshInstance, NotifyMixin): +class ConfigurationMixin(object): + """Configuration Mixin + + Configuration related tasks for instances and resizes. + """ + + def _render_config(self, service_type, flavor): + config = template.SingleInstanceConfigTemplate( + service_type, flavor) + config.render() + return config + + +class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): def create_instance(self, flavor, image_id, databases, users, service_type, volume_size, security_groups, @@ -136,9 +150,13 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin): err = inst_models.InstanceTasks.BUILDING_ERROR_DNS self._log_and_raise(e, msg, err) + config = self._render_config(service_type, flavor) + if server: self._guest_prepare(server, flavor['ram'], volume_info, - databases, users, backup_id) + databases, users, backup_id, + config.config_location, + config.config_contents) if not self.db_info.task_status.is_error: self.update_db(task_status=inst_models.InstanceTasks.NONE) @@ -331,13 +349,16 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin): return server def _guest_prepare(self, server, flavor_ram, volume_info, - databases, users, backup_id=None): + databases, users, backup_id=None, + config_location=None, config_contents=None): LOG.info("Entering guest_prepare.") # Now wait for the response from the create to do additional work self.guest.prepare(flavor_ram, databases, users, device_path=volume_info['device_path'], mount_point=volume_info['mount_point'], - backup_id=backup_id) + backup_id=backup_id, + config_location=config_location, + config_contents=config_contents) def _create_dns_entry(self): LOG.debug("%s: Creating dns entry for instance: %s" % @@ -377,7 +398,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin): (greenthread.getcurrent(), self.id)) -class BuiltInstanceTasks(BuiltInstance, NotifyMixin): +class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin): """ Performs the various asynchronous instance related tasks. """ @@ -563,7 +584,7 @@ class BackupTasks(object): backup.delete() -class ResizeActionBase(object): +class ResizeActionBase(ConfigurationMixin): """Base class for executing a resize action.""" def __init__(self, instance): @@ -711,7 +732,10 @@ class ResizeAction(ResizeActionBase): % self.instance.id) LOG.debug("Repairing config.") try: - config = {'memory_mb': self.old_flavor['ram']} + config = self._render_config(self.instance.service_type, + self.old_flavor) + config = {'config_location': config.config_location, + 'config_contents': config.config_contents} self.instance.guest.reset_configuration(config) except GuestTimeout as gt: LOG.exception("Error sending reset_configuration call.") @@ -730,7 +754,10 @@ class ResizeAction(ResizeActionBase): modify_at=timeutils.isotime()) def _start_mysql(self): - self.instance.guest.start_db_with_conf_changes(self.new_flavor['ram']) + config = self._render_config(self.instance.service_type, + self.new_flavor) + self.instance.guest.start_db_with_conf_changes(config.config_location, + config.config_contents) class MigrateAction(ResizeActionBase): diff --git a/trove/templates/mysql.config.template b/trove/templates/mysql.config.template new file mode 100644 index 0000000000..1bd6aae7c0 --- /dev/null +++ b/trove/templates/mysql.config.template @@ -0,0 +1,58 @@ +[client] +port = 3306 +socket = /var/run/mysqld/mysqld.sock + +[mysqld_safe] +socket = /var/run/mysqld/mysqld.sock +nice = 0 + +[mysqld] +user = mysql +socket = /var/run/mysqld/mysqld.sock +port = 3306 +basedir = /usr +datadir = /var/lib/mysql +####tmpdir = /tmp +tmpdir = /var/tmp +pid_file = /var/run/mysqld/mysqld.pid +skip-external-locking +key_buffer_size = {{ 50 * flavor['ram']//512 }}M +max_allowed_packet = {{ 1 * flavor['ram']//512 }}M +thread_stack = 192K +thread_cache_size = {{ 4 * flavor['ram']//512 }} +myisam-recover = BACKUP +query_cache_type = 1 +query_cache_limit = 1M +query_cache_size = {{ 8 * flavor['ram']//512 }}M +log_error = /var/log/mysql/mysqld.log +innodb_data_file_path = ibdata1:10M:autoextend +innodb_buffer_pool_size = {{ 150 * flavor['ram']//512 }}M +innodb_file_per_table = 1 +innodb_log_files_in_group = 2 +innodb_log_file_size=50M +innodb_log_buffer_size=25M +connect_timeout = 15 +wait_timeout = 120 +join_buffer_size = 1M +read_buffer_size = 512K +read_rnd_buffer_size = 512K +sort_buffer_size = 1M +tmp_table_size = {{ 16 * flavor['ram']//512 }}M +max_heap_table_size = {{ 16 * flavor['ram']//512 }}M +table_open_cache = {{ 256 * flavor['ram']//512 }} +table_definition_cache = {{ 256 * flavor['ram']//512 }} +open_files_limit = {{ 512 * flavor['ram']//512 }} +max_user_connections = {{ 100 * flavor['ram']//512 }} +max_connections = {{ 100 * flavor['ram']//512 }} +default_storage_engine = innodb +local-infile = 0 + +[mysqldump] +quick +quote-names +max_allowed_packet = 16M + +[isamchk] +key_buffer = 16M + +!includedir /etc/mysql/conf.d/ diff --git a/trove/tests/api/instances_resize.py b/trove/tests/api/instances_resize.py index b68fd90199..c6c16f1e23 100644 --- a/trove/tests/api/instances_resize.py +++ b/trove/tests/api/instances_resize.py @@ -21,6 +21,7 @@ from novaclient.exceptions import BadRequest from novaclient.v1_1.servers import Server from trove.common.exception import PollTimeOut +from trove.common import template from trove.common import utils from trove.common.context import TroveContext from trove.guestagent import api as guest @@ -114,7 +115,11 @@ class ResizeTests(ResizeTestBase): self._teardown() def _start_mysql(self): - self.instance.guest.start_db_with_conf_changes(NEW_FLAVOR.ram) + config = template.SingleInstanceConfigTemplate( + "mysql", NEW_FLAVOR.__dict__) + config.render() + self.instance.guest.start_db_with_conf_changes(config.config_location, + config.config_contents) def test_guest_wont_stop_mysql(self): self.guest.stop_db(do_not_start_on_reboot=True)\ diff --git a/trove/tests/fakes/guestagent.py b/trove/tests/fakes/guestagent.py index 2a59134cbb..3703b74a27 100644 --- a/trove/tests/fakes/guestagent.py +++ b/trove/tests/fakes/guestagent.py @@ -181,7 +181,8 @@ class FakeGuest(object): return self.users.get((username, hostname), None) def prepare(self, memory_mb, databases, users, device_path=None, - mount_point=None, backup_id=None): + mount_point=None, backup_id=None, config_location=None, + config_contents=None): from trove.instance.models import DBInstance from trove.instance.models import InstanceServiceStatus from trove.instance.models import ServiceStatuses @@ -225,7 +226,7 @@ class FakeGuest(object): # There's nothing to do here, since there is no config to update. pass - def start_db_with_conf_changes(self, updated_memory_size): + def start_db_with_conf_changes(self, config_location, config_contents): time.sleep(2) self._set_status('RUNNING') diff --git a/trove/tests/unittests/common/test_template.py b/trove/tests/unittests/common/test_template.py new file mode 100644 index 0000000000..ad4a59775d --- /dev/null +++ b/trove/tests/unittests/common/test_template.py @@ -0,0 +1,57 @@ +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +#http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. + + +import testtools +import re + +from trove.common import template +from trove.tests.unittests.util import util + + +class TemplateTest(testtools.TestCase): + def setUp(self): + super(TemplateTest, self).setUp() + util.init_db() + self.env = template.get_env() + self.template = self.env.get_template("mysql.config.template") + self.flavor_dict = {'ram': 1024} + + def tearDown(self): + super(TemplateTest, self).tearDown() + + def validate_template(self, contents, teststr, test_flavor): + # expected query_cache_size = {{ 8 * flavor_multiplier }}M + flavor_multiplier = test_flavor['ram'] / 512 + found_group = None + for line in contents.split('\n'): + m = re.search('^%s.*' % teststr, line) + if m: + found_group = m.group(0) + if not found_group: + raise "Could not find text in template" + # Check that the last group has been rendered + memsize = found_group.split(" ")[2] + self.assertEqual(memsize, "%sM" % (8 * flavor_multiplier)) + + def test_rendering(self): + rendered = self.template.render(flavor=self.flavor_dict) + self.validate_template(rendered, "query_cache_size", self.flavor_dict) + + def test_single_instance_config_rendering(self): + location = "/etc/mysql/my.cnf" + config = template.SingleInstanceConfigTemplate('mysql', + self.flavor_dict) + config.render() + self.assertEqual(location, config.config_location) + self.validate_template(config.config_contents, "query_cache_size", + self.flavor_dict) diff --git a/trove/tests/unittests/guestagent/test_api.py b/trove/tests/unittests/guestagent/test_api.py index e4813dc697..58a93668bb 100644 --- a/trove/tests/unittests/guestagent/test_api.py +++ b/trove/tests/unittests/guestagent/test_api.py @@ -177,9 +177,9 @@ class ApiTest(testtools.TestCase): def test_start_db_with_conf_changes(self): exp_msg = RpcMsgMatcher('start_db_with_conf_changes', - 'updated_memory_size') + 'config_location', 'config_contents') self._mock_rpc_call(exp_msg) - self.api.start_db_with_conf_changes('512') + self.api.start_db_with_conf_changes(None, None) self._verify_rpc_call(exp_msg) def test_stop_db(self): @@ -218,12 +218,13 @@ class ApiTest(testtools.TestCase): when(rpc).create_connection(new=True).thenReturn(mock_conn) when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None) exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users', - 'device_path', 'mount_point', 'backup_id') + 'device_path', 'mount_point', 'backup_id', + 'config_location', 'config_contents') when(rpc).cast(any(), any(), exp_msg).thenReturn(None) self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt', - 'bkup-1232') + 'bkup-1232', 'loc', 'cont') self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg) @@ -232,11 +233,12 @@ class ApiTest(testtools.TestCase): when(rpc).create_connection(new=True).thenReturn(mock_conn) when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None) exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users', - 'device_path', 'mount_point', 'backup_id') + 'device_path', 'mount_point', 'backup_id', + 'config_location', 'config_contents') when(rpc).cast(any(), any(), exp_msg).thenReturn(None) self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt', - 'backup_id_123') + 'backup_id_123', 'loc', 'cont') self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg) diff --git a/trove/tests/unittests/guestagent/test_dbaas.py b/trove/tests/unittests/guestagent/test_dbaas.py index 7985d308b0..a4a06d12ac 100644 --- a/trove/tests/unittests/guestagent/test_dbaas.py +++ b/trove/tests/unittests/guestagent/test_dbaas.py @@ -61,6 +61,7 @@ FAKE_DB_2 = {"_name": "testDB2", "_character_set": "latin2", "_collate": "latin2_general_ci"} FAKE_USER = [{"_name": "random", "_password": "guesswhat", "_databases": [FAKE_DB]}] +MYCNF = '/etc/mysql/my.cnf' class FakeAppStatus(MySqlAppStatus): @@ -595,7 +596,7 @@ class MySqlAppTest(testtools.TestCase): self.mysql_starts_successfully() self.appStatus.status = ServiceStatuses.SHUTDOWN - self.mySqlApp.start_db_with_conf_changes(Mock()) + self.mySqlApp.start_db_with_conf_changes(Mock(), Mock()) self.assertTrue(self.mySqlApp._write_mycnf.called) self.assertTrue(self.mySqlApp.start_mysql.called) @@ -609,7 +610,8 @@ class MySqlAppTest(testtools.TestCase): self.appStatus.status = ServiceStatuses.RUNNING self.assertRaises(RuntimeError, - self.mySqlApp.start_db_with_conf_changes, Mock()) + self.mySqlApp.start_db_with_conf_changes, + Mock(), Mock()) class MySqlAppInstallTest(MySqlAppTest): @@ -641,7 +643,7 @@ class MySqlAppInstallTest(MySqlAppTest): self.mysql_starts_successfully() sqlalchemy.create_engine = Mock() - self.mySqlApp.secure(100) + self.mySqlApp.secure(MYCNF, 'contents') self.assertTrue(self.mySqlApp.stop_db.called) self.assertTrue(self.mySqlApp._write_mycnf.called) @@ -668,13 +670,13 @@ class MySqlAppInstallTest(MySqlAppTest): self.mySqlApp.start_mysql = Mock() self.mySqlApp.stop_db = Mock() self.mySqlApp._write_mycnf = \ - Mock(side_effect=pkg.PkgPackageStateError("Install error")) + Mock(side_effect=IOError("Could not write file")) self.mysql_stops_successfully() self.mysql_starts_successfully() sqlalchemy.create_engine = Mock() - self.assertRaises(pkg.PkgPackageStateError, - self.mySqlApp.secure, 100) + self.assertRaises(IOError, + self.mySqlApp.secure, "/etc/mycnf/my.cnf", "foo") self.assertTrue(self.mySqlApp.stop_db.called) self.assertTrue(self.mySqlApp._write_mycnf.called) @@ -731,26 +733,6 @@ def mock_admin_sql_connection(): class MySqlAppMockTest(testtools.TestCase): - @classmethod - def stub_file(cls, filename): - return MySqlAppMockTest.StubFile(filename) - - class StubFile(object): - def __init__(self, filename): - when(__builtin__).open(filename, any()).thenReturn(self) - - def next(self): - raise StopIteration - - def __iter__(self): - return self - - def write(self, data): - pass - - def close(self): - pass - def tearDown(self): super(MySqlAppMockTest, self).tearDown() unstub() @@ -760,8 +742,6 @@ class MySqlAppMockTest(testtools.TestCase): when(mock_conn).execute(any()).thenReturn(None) when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn( None) - when(pkg).pkg_install("dbaas-mycnf", any()).thenRaise( - pkg.PkgPackageStateError("Install error")) # skip writing the file for now when(os.path).isfile(any()).thenReturn(False) mock_status = mock(MySqlAppStatus) @@ -769,7 +749,7 @@ class MySqlAppMockTest(testtools.TestCase): any(), any(), any()).thenReturn(True) app = MySqlApp(mock_status) - self.assertRaises(pkg.PkgPackageStateError, app.secure, 2048) + self.assertRaises(TypeError, app.secure, MYCNF, None) verify(mock_conn, atleast=2).execute(any()) inorder.verify(mock_status).wait_for_real_status_to_change_to( @@ -782,20 +762,16 @@ class MySqlAppMockTest(testtools.TestCase): when(mock_conn).execute(any()).thenReturn(None) when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn( None) - when(pkg).pkg_install("dbaas-mycnf", any()).thenReturn(None) # skip writing the file for now when(os.path).isfile(any()).thenReturn(False) when(utils).execute_with_timeout( "sudo", "chmod", any(), any()).thenReturn(None) - MySqlAppMockTest.stub_file("/etc/mysql/my.cnf") - MySqlAppMockTest.stub_file("/etc/dbaas/my.cnf/my.cnf.2048M") - MySqlAppMockTest.stub_file("/tmp/my.cnf.tmp") mock_status = mock(MySqlAppStatus) when(mock_status).wait_for_real_status_to_change_to( any(), any(), any()).thenReturn(True) app = MySqlApp(mock_status) - - app.secure(2048) + when(app)._write_mycnf(any(), any()).thenReturn(True) + app.secure(MYCNF, 'foo') verify(mock_conn, never).execute(TextClauseMatcher('root')) diff --git a/trove/tests/unittests/guestagent/test_manager.py b/trove/tests/unittests/guestagent/test_manager.py index b7a36934b1..f5bf4add35 100644 --- a/trove/tests/unittests/guestagent/test_manager.py +++ b/trove/tests/unittests/guestagent/test_manager.py @@ -181,7 +181,8 @@ class GuestAgentManagerTest(testtools.TestCase): if backup_id: verify(backup).restore(self.context, backup_id, '/var/lib/mysql') verify(dbaas.MySqlApp).install_if_needed() - verify(dbaas.MySqlApp).secure('2048') + # We dont need to make sure the exact contents are there + verify(dbaas.MySqlApp).secure(any(), any()) verify(dbaas.MySqlAdmin, never).create_database() verify(dbaas.MySqlAdmin, never).create_user() times_report = 1 if is_root_enabled else 0