Skip to content

Commit f293b47

Browse files
[DRAFT]
1 parent 31f85f2 commit f293b47

File tree

4 files changed

+53
-8
lines changed

4 files changed

+53
-8
lines changed

src/mysql_shell/__init__.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
if typing.TYPE_CHECKING:
2222
import relations.database_requires
2323

24+
ROLE_DML = "charmed_dml"
25+
ROLE_READ = "charmed_read"
26+
2427
logger = logging.getLogger(__name__)
2528

2629

@@ -125,17 +128,30 @@ def _get_attributes(self, additional_attributes: dict = None) -> str:
125128
attributes.update(additional_attributes)
126129
return json.dumps(attributes)
127130

128-
def create_application_database_and_user(self, *, username: str, database: str) -> str:
129-
"""Create database and user for related database_provides application."""
131+
def create_application_database(self, *, database: str) -> str:
132+
"""Create database for related database_provides application."""
133+
mysql_roles = self.get_mysql_roles("charmed_%")
134+
statements = [f"CREATE DATABASE IF NOT EXISTS `{database}`"]
135+
if ROLE_READ in mysql_roles:
136+
statements.append(f"GRANT SELECT ON `{database}`.* TO {ROLE_READ}")
137+
if ROLE_DML in mysql_roles:
138+
statements.append(f"GRANT INSERT, DELETE, UPDATE ON `{database}`.* TO {ROLE_DML}")
139+
140+
logger.debug(f"Creating {database=}")
141+
self._run_sql(statements)
142+
logger.debug(f"Created {database=}")
143+
return database
144+
145+
def create_application_user(self, *, database: str, username: str) -> str:
146+
"""Create database user for related database_provides application."""
130147
attributes = self._get_attributes()
131-
logger.debug(f"Creating {database=} and {username=} with {attributes=}")
132148
password = utils.generate_password()
149+
logger.debug(f"Creating {username=} with {attributes=}")
133150
self._run_sql([
134-
f"CREATE DATABASE IF NOT EXISTS `{database}`",
135151
f"CREATE USER `{username}` IDENTIFIED BY '{password}' ATTRIBUTE '{attributes}'",
136152
f"GRANT ALL PRIVILEGES ON `{database}`.* TO `{username}`",
137153
])
138-
logger.debug(f"Created {database=} and {username=} with {attributes=}")
154+
logger.debug(f"Created {username=} with {attributes=}")
139155
return password
140156

141157
def add_attributes_to_mysql_router_user(
@@ -150,6 +166,23 @@ def add_attributes_to_mysql_router_user(
150166
self._run_sql([f"ALTER USER `{username}` ATTRIBUTE '{attributes}'"])
151167
logger.debug(f"Added {attributes=} to {username=}")
152168

169+
# TODO python3.10 min version: Use `set` instead of `typing.Set`
170+
def get_mysql_roles(self, name_pattern: str) -> typing.Set[str]:
171+
"""Returns a set with the MySQL roles."""
172+
logger.debug(f"Getting MySQL roles with {name_pattern=}")
173+
output_file = self._container.path("/tmp/mysqlsh_output.json")
174+
self._run_code(
175+
_jinja_env.get_template("get_mysql_roles_with_pattern.py.jinja").render(
176+
name_pattern=name_pattern,
177+
output_filepath=output_file.relative_to_container,
178+
)
179+
)
180+
with output_file.open("r") as file:
181+
rows = json.load(file)
182+
output_file.unlink()
183+
logger.debug(f"MySQL roles found for {name_pattern=}: {len(rows)}")
184+
return set(rows)
185+
153186
def get_mysql_router_user_for_unit(
154187
self, unit_name: str
155188
) -> typing.Optional[RouterUserInformation]:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import json
2+
3+
result = session.run_sql(
4+
"SELECT user FROM mysql.user WHERE user LIKE '{{ name_pattern }}'"
5+
)
6+
rows = result.fetch_all()
7+
# mysqlsh objects are weird—they quack (i.e. duck typing) like standard Python objects (e.g. list,
8+
# dict), but do not serialize to JSON correctly.
9+
# Cast to str & load from JSON str before serializing
10+
rows = json.loads(str(rows))
11+
with open("{{ output_filepath }}", "w") as file:
12+
json.dump(rows, file)

src/relations/database_provides.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,8 @@ def create_database_and_user(
116116
shell.delete_user(username, must_exist=False)
117117
logger.debug("Deleted user if exists before creating user")
118118

119-
password = shell.create_application_database_and_user(
120-
username=username, database=self._database
121-
)
119+
________ = shell.create_application_database(database=self._database)
120+
password = shell.create_application_user(database=self._database, username=username)
122121

123122
self._set_databag(
124123
username=username,

tests/unit/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def patch(monkeypatch):
3333
)
3434
monkeypatch.setattr("workload.AuthenticatedWorkload._router_username", "")
3535
monkeypatch.setattr("mysql_shell.Shell._run_code", lambda *args, **kwargs: None)
36+
monkeypatch.setattr("mysql_shell.Shell.get_mysql_roles", lambda *args, **kwargs: set())
3637
monkeypatch.setattr(
3738
"mysql_shell.Shell.get_mysql_router_user_for_unit", lambda *args, **kwargs: None
3839
)

0 commit comments

Comments
 (0)