From 114fb0805139224d2f281b959a187c80c6ede782 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Mon, 28 Jun 2021 20:01:40 +0100 Subject: [PATCH] Ensure update config function can handle new headers The linked bug occurred due to a new header for the config file being in the upgraded charm, but not in the previous version of the charm. As the config file is read, updated, and then written, new headings in the INI file would crash the charm code with a KeyError. This patch just sets the header and parameter if it is missing. Closes-Bug: #1927981 Change-Id: I18a6a4143ee0a1144eade5caa50611b802cba28a --- src/lib/charm/openstack/mysql_router.py | 7 ++++- .../test_lib_charm_openstack_mysql_router.py | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/lib/charm/openstack/mysql_router.py b/src/lib/charm/openstack/mysql_router.py index fa91b48..5e99d14 100644 --- a/src/lib/charm/openstack/mysql_router.py +++ b/src/lib/charm/openstack/mysql_router.py @@ -635,7 +635,12 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm): config.read(self.mysqlrouter_conf) for heading in parameters.keys(): for param in parameters[heading].keys(): - config[heading][param] = parameters[heading][param] + # BUG LP#1927981 - heading may not exist during a charm upgrade + # Handle missing heading via direct assignment in except. + try: + config[heading][param] = parameters[heading][param] + except KeyError: + config[heading] = {param: parameters[heading][param]} ch_core.hookenv.log("Writing {}".format( self.mysqlrouter_conf)) with open(self.mysqlrouter_conf, 'w') as configfile: diff --git a/unit_tests/test_lib_charm_openstack_mysql_router.py b/unit_tests/test_lib_charm_openstack_mysql_router.py index b241646..83a5173 100644 --- a/unit_tests/test_lib_charm_openstack_mysql_router.py +++ b/unit_tests/test_lib_charm_openstack_mysql_router.py @@ -661,6 +661,35 @@ class TestMySQLRouterCharm(test_utils.PatchHelper): _mock_config_parser.write.assert_called_once_with( self.mock_open()().__enter__()) + def test_update_config_parameters_missing_heading(self): + # test fix for Bug LP#1927981 + class FakeConfigParser(dict): + def read(*args, **kwargs): + pass + + def write(*args, **kwargs): + pass + + current_config = {"DEFAULT": {"client_ssl_mode": "NONE"}} + fake_config = FakeConfigParser(current_config) + + self.patch_object(mysql_router.configparser, "ConfigParser", + return_value=fake_config) + + # metadata_cache:jujuCluster didn't exist in the previous config so the + # header needs to be created (c.f. BUG LP#1927981) + _params = { + "DEFAULT": {"client_ssl_mode": "PREFERRED"}, + "metadata_cache:jujuCluster": {"thing": "a-thing"}, + } + + mrc = mysql_router.MySQLRouterCharm() + # should not throw a key error. + mrc.update_config_parameters(_params) + self.assertIn('metadata_cache:jujuCluster', fake_config) + self.assertEqual(fake_config['metadata_cache:jujuCluster'], + {"thing": "a-thing"}) + def test_config_changed(self): _config_data = { "ttl": '5',