Create and use the mysql system user

Change-Id: Ife32e896a964596903ae24b1f0a36a956957747f
This commit is contained in:
David Ames 2019-12-13 15:27:22 -08:00
parent 91f80513db
commit 454f6e9f8b
3 changed files with 88 additions and 21 deletions

View File

@ -14,11 +14,6 @@ options:
.
See https://wiki.ubuntu.com/OpenStack/CloudArchive for info on which
cloud archives are available and supported.
system-user:
# TODO What user? No mysql user exists. Create one?
type: string
description: System user to run mysqlrouter
default: ubuntu
base-port:
type: int
default: 3306

View File

@ -13,6 +13,7 @@
# limitations under the License.
import json
import os
import subprocess
import charms_openstack.charm
@ -171,7 +172,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
return self.options.shared_db_address
@property
def mysqlrouter_dir(self):
def mysqlrouter_working_dir(self):
"""Determine the path to the mysqlrouter working directory.
:param self: Self
@ -179,7 +180,26 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
:returns: Path to the directory
:rtype: str
"""
return "/home/{}/mysqlrouter".format(self.options.system_user)
return "{}/mysqlrouter".format(self.mysqlrouter_home_dir)
@property
def mysqlrouter_home_dir(self):
"""Determine the path to the mysqlrouter working directory.
:param self: Self
:type self: MySQLRouterCharm instance
:returns: Path to the directory
:rtype: str
"""
return "/var/lib/mysql"
@property
def mysqlrouter_user(self):
return "mysql"
@property
def mysqlrouter_group(self):
return "mysql"
def install(self):
"""Custom install function.
@ -195,6 +215,27 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
self.configure_source()
super().install()
# Neither MySQL Router nor MySQL common packaging creates a user, group
# or home dir. As we want it to run as a system user in a predictable
# location create all of these.
# Create the group
if not ch_core.host.group_exists(self.mysqlrouter_group):
ch_core.host.add_group(
self.mysqlrouter_group, system_group=True)
# Create the user
if not ch_core.host.user_exists(self.mysqlrouter_user):
ch_core.host.adduser(
self.mysqlrouter_user, shell="/usr/sbin/nologin",
system_user=True, primary_group=self.mysqlrouter_group,
home_dir=self.mysqlrouter_home_dir)
# Create the directory
if not os.path.exists(self.mysqlrouter_home_dir):
ch_core.host.mkdir(
self.mysqlrouter_home_dir,
owner=self.mysqlrouter_user,
group=self.mysqlrouter_group,
perms=0o755)
def get_db_helper(self):
"""Get an instance of the MySQLDB8Helper class.
@ -307,12 +348,12 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
:rtype: None
"""
cmd = [self.mysqlrouter_bin,
"--user", self.options.system_user,
"--user", self.mysqlrouter_user,
"--bootstrap",
"{}:{}@{}".format(self.db_router_user,
self.db_router_password,
self.cluster_address),
"--directory", self.mysqlrouter_dir,
"--directory", self.mysqlrouter_working_dir,
"--conf-use-sockets",
"--conf-base-port", str(self.options.base_port)]
try:
@ -336,7 +377,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
:returns: This function is called for its side effect
:rtype: None
"""
cmd = ["{}/start.sh".format(self.mysqlrouter_dir)]
cmd = ["{}/start.sh".format(self.mysqlrouter_working_dir)]
try:
proc = subprocess.Popen(
cmd, stdout=subprocess.PIPE,
@ -362,7 +403,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
:returns: This function is called for its side effect
:rtype: None
"""
cmd = ["{}/stop.sh".format(self.mysqlrouter_dir)]
cmd = ["{}/stop.sh".format(self.mysqlrouter_working_dir)]
try:
proc = subprocess.Popen(
cmd, stdout=subprocess.PIPE,

View File

@ -61,6 +61,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
def setUp(self):
super().setUp()
self.patch_object(mysql_router, "os")
self.patch_object(mysql_router, "subprocess")
self.patch_object(mysql_router.reactive.flags, "set_flag")
self.patch_object(mysql_router.reactive.flags, "clear_flag")
@ -68,6 +69,11 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
mysql_router.reactive.relations, "endpoint_from_flag")
self.patch_object(mysql_router.ch_net_ip, "get_relation_ip")
self.patch_object(mysql_router.ch_core.hookenv, "local_unit")
self.patch_object(mysql_router.ch_core.host, "adduser")
self.patch_object(mysql_router.ch_core.host, "add_group")
self.patch_object(mysql_router.ch_core.host, "user_exists")
self.patch_object(mysql_router.ch_core.host, "group_exists")
self.patch_object(mysql_router.ch_core.host, "mkdir")
self.stdout = mock.MagicMock()
self.subprocess.STDOUT = self.stdout
@ -210,23 +216,48 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
mrc.shared_db_address,
"127.0.0.1")
def test_mysqlrouter_dir(self):
_user = "ubuntu"
def test_mysqlrouter_working_dir(self):
mrc = mysql_router.MySQLRouterCharm()
mrc.options.system_user = _user
self.assertEqual(
mrc.mysqlrouter_dir,
"/home/{}/mysqlrouter".format(_user))
mrc.mysqlrouter_working_dir,
"/var/lib/mysql/mysqlrouter")
def test_mysqlrouter_home_dir(self):
mrc = mysql_router.MySQLRouterCharm()
self.assertEqual(
mrc.mysqlrouter_home_dir,
"/var/lib/mysql")
def test_mysqlrouter_group(self):
mrc = mysql_router.MySQLRouterCharm()
self.assertEqual(
mrc.mysqlrouter_group,
"mysql")
def test_mysqlrouter_user(self):
mrc = mysql_router.MySQLRouterCharm()
self.assertEqual(
mrc.mysqlrouter_user,
"mysql")
def test_install(self):
self.patch_object(
mysql_router.charms_openstack.charm.OpenStackCharm,
"install", "super_install")
self.os.path.exists.return_value = False
self.group_exists.return_value = False
self.user_exists.return_value = False
mrc = mysql_router.MySQLRouterCharm()
mrc.configure_source = mock.MagicMock()
mrc.install()
self.super_install.assert_called_once()
mrc.configure_source.assert_called_once()
self.add_group.assert_called_once_with("mysql", system_group=True)
self.adduser.assert_called_once_with(
"mysql", home_dir="/var/lib/mysql", primary_group="mysql",
shell="/usr/sbin/nologin", system_user=True)
self.mkdir.assert_called_once_with(
"/var/lib/mysql", group="mysql", owner="mysql", perms=0o755)
def test_get_db_helper(self):
self.patch_object(
@ -325,7 +356,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
_json_pass = '"clusterpass"'
_pass = json.loads(_json_pass)
_addr = json.loads(_json_addr)
_user = "ubuntu"
_user = "mysql"
_port = "3006"
self.endpoint_from_flag.return_value = self.db_router
self.db_router.password.return_value = _json_pass
@ -340,7 +371,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
self.subprocess.check_output.assert_called_once_with(
[mrc.mysqlrouter_bin, "--user", _user, "--bootstrap",
"{}:{}@{}".format(mrc.db_router_user, _pass, _addr),
"--directory", mrc.mysqlrouter_dir, "--conf-use-sockets",
"--directory", mrc.mysqlrouter_working_dir, "--conf-use-sockets",
"--conf-base-port", _port],
stderr=self.stdout)
self.set_flag.assert_called_once_with(
@ -356,7 +387,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
self.set_flag.assert_not_called()
def test_start_mysqlrouter(self):
_user = "ubuntu"
_user = "mysql"
_port = "3006"
mrc = mysql_router.MySQLRouterCharm()
mrc.options.system_user = _user
@ -365,7 +396,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
# Successful
mrc.start_mysqlrouter()
self.subprocess.Popen.assert_called_once_with(
["/home/ubuntu/mysqlrouter/start.sh"],
["/var/lib/mysql/mysqlrouter/start.sh"],
bufsize=1,
stdout=self.stdout,
stderr=self.stdout,
@ -391,7 +422,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
# Successful
mrc.stop_mysqlrouter()
self.subprocess.Popen.assert_called_once_with(
["/home/ubuntu/mysqlrouter/stop.sh"],
["/var/lib/mysql/mysqlrouter/stop.sh"],
bufsize=1,
stdout=self.stdout,
stderr=self.stdout,