Use force on subsequent attempts to add instance

If for any reason the first attempt to add an instance to cluster fails,
subsequent attempts would fail with:

Error: It appears that a router instance named <name> has been
previously configured in this host. If that instance no longer exists,
use the --force option to overwrite it.

This change avoids this error by adding the --force switch on subsequent
attempts.

Closes-Bug: #1919560

Change-Id: Icb0b10d6a5e3fe902c7ea56dbcb2f04d1129cd5f
This commit is contained in:
David Ames 2021-03-19 15:15:01 -07:00
parent 8a36a6a433
commit 89595f1f8b
2 changed files with 70 additions and 6 deletions

View File

@ -33,6 +33,7 @@ import charmhelpers.contrib.openstack.templating as os_templating
# Flag Strings
MYSQL_ROUTER_BOOTSTRAPPED = "charm.mysqlrouter.bootstrapped"
MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED = "charm.mysqlrouter.bootstrap-attempted"
MYSQL_ROUTER_STARTED = "charm.mysqlrouter.started"
DB_ROUTER_AVAILABLE = "db-router.available"
DB_ROUTER_PROXY_AVAILABLE = "db-router.available.proxy"
@ -410,6 +411,16 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
:returns: This function is called for its side effect
:rtype: None
"""
if reactive.flags.is_flag_set(MYSQL_ROUTER_BOOTSTRAPPED):
ch_core.hookenv.log(
"Bootstrap mysqlrouter is being called after we set the "
"bootstrapped flag: {}. This may require manual intervention,"
"bailing for now."
.format(MYSQL_ROUTER_BOOTSTRAPPED),
"WARNING")
return
cmd = [self.mysqlrouter_bin,
"--user", self.mysqlrouter_user,
"--name", self.name,
@ -426,6 +437,14 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
# Bug #1911907
if ch_core.host.cmp_pkgrevno('mysql-router', '8.0.22') >= 0:
cmd.append("--disable-rest")
# If we have attempted to bootstrap before but unsuccessfully,
# use the force option to avoid LP Bug#1919560
if reactive.flags.is_flag_set(MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED):
cmd.append("--force")
# Set and attempt the bootstrap
reactive.flags.set_flag(MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED)
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
ch_core.hookenv.log(output, "DEBUG")
@ -434,6 +453,9 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
"Failed to bootstrap mysqlrouter: {}"
.format(e.output.decode("UTF-8")), "ERROR")
return
# Clear the attempted flag as we were successful
reactive.flags.clear_flag(MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED)
# Set that we have been bootstrapped
reactive.flags.set_flag(MYSQL_ROUTER_BOOTSTRAPPED)
def start_mysqlrouter(self):

View File

@ -375,9 +375,11 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
_addr = json.loads(_json_addr)
_user = "mysql"
_port = "3006"
self.patch_object(mysql_router.reactive.flags, "is_flag_set")
self.endpoint_from_flag.return_value = self.db_router
self.db_router.password.return_value = _json_pass
self.db_router.db_host.return_value = _json_addr
self.is_flag_set.return_value = False
mrc = mysql_router.MySQLRouterCharm()
mrc.options.system_user = _user
@ -396,12 +398,16 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
"--report-host", mrc.db_router_address,
"--conf-base-port", _port],
stderr=self.stdout)
self.set_flag.assert_called_once_with(
mysql_router.MYSQL_ROUTER_BOOTSTRAPPED)
self.set_flag.assert_has_calls([
mock.call(mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED),
mock.call(mysql_router.MYSQL_ROUTER_BOOTSTRAPPED)])
self.clear_flag.assert_called_once_with(
mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED)
# Successful >= 8.0.22
self.subprocess.reset_mock()
self.set_flag.reset_mock()
self.clear_flag.reset_mock()
self.cmp_pkgrevno.return_value = 1
mrc.bootstrap_mysqlrouter()
self.subprocess.check_output.assert_called_once_with(
@ -415,17 +421,53 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
"--conf-base-port", _port,
"--disable-rest"],
stderr=self.stdout)
self.set_flag.assert_called_once_with(
mysql_router.MYSQL_ROUTER_BOOTSTRAPPED)
self.set_flag.assert_has_calls([
mock.call(mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED),
mock.call(mysql_router.MYSQL_ROUTER_BOOTSTRAPPED)])
self.clear_flag.assert_called_once_with(
mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED)
# Fail
# First attempt fail
self.subprocess.reset_mock()
self.set_flag.reset_mock()
self.subprocess.CalledProcessError = FakeException
self.subprocess.check_output.side_effect = (
self.subprocess.CalledProcessError)
mrc.bootstrap_mysqlrouter()
self.set_flag.assert_not_called()
self.set_flag.assert_called_once_with(
mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED)
# Bail
self.subprocess.reset_mock()
self.set_flag.reset_mock()
self.is_flag_set.return_value = True
mrc.bootstrap_mysqlrouter()
self.subprocess.check_output.assert_not_called()
# Second attempt success
self.subprocess.reset_mock()
self.set_flag.reset_mock()
self.clear_flag.reset_mock()
self.cmp_pkgrevno.return_value = 1
self.is_flag_set.side_effect = [False, True]
self.subprocess.check_output.side_effect = None
mrc.bootstrap_mysqlrouter()
self.subprocess.check_output.assert_called_once_with(
[mrc.mysqlrouter_bin, "--user", _user, "--name", mrc.name,
"--bootstrap", "{}:{}@{}"
.format(mrc.db_router_user, _pass, _addr),
"--directory", mrc.mysqlrouter_working_dir,
"--conf-use-sockets",
"--conf-bind-address", mrc.shared_db_address,
"--report-host", mrc.db_router_address,
"--conf-base-port", _port,
"--disable-rest", "--force"],
stderr=self.stdout)
self.set_flag.assert_has_calls([
mock.call(mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED),
mock.call(mysql_router.MYSQL_ROUTER_BOOTSTRAPPED)])
self.clear_flag.assert_called_once_with(
mysql_router.MYSQL_ROUTER_BOOTSTRAP_ATTEMPTED)
def test_start_mysqlrouter(self):
self.patch_object(mysql_router.ch_core.host, "service_start")