diff --git a/tests/commands/test_jobs.py b/tests/commands/test_jobs.py index bbd3aaadca..dfee31119f 100644 --- a/tests/commands/test_jobs.py +++ b/tests/commands/test_jobs.py @@ -91,17 +91,13 @@ def test_set_theme(self) -> None: self.assertIn("assign_theme('beautiful', 'domain1')", dc_args[-1]) self.assertIn("assign_theme('beautiful', 'domain2')", dc_args[-1]) - def test_update_mysql_authentication_plugin(self) -> None: + def test_update_mysql_authentication_plugin_all_users(self) -> None: with temporary_root() as root: self.invoke_in_root(root, ["config", "save"]) with patch("tutor.utils.docker_compose") as mock_docker_compose: result = self.invoke_in_root( root, - [ - "local", - "do", - "update-mysql-authentication-plugin", - ], + ["local", "do", "update-mysql-authentication-plugin", "all"], ) dc_args, _dc_kwargs = mock_docker_compose.call_args @@ -111,3 +107,19 @@ def test_update_mysql_authentication_plugin(self) -> None: self.assertIn("caching_sha2_password", dc_args[-1]) self.assertIn("openedx", dc_args[-1]) self.assertIn("root", dc_args[-1]) + + def test_update_mysql_authentication_plugin_one_user(self) -> None: + with temporary_root() as root: + self.invoke_in_root(root, ["config", "save"]) + with patch("tutor.utils.docker_compose") as mock_docker_compose: + result = self.invoke_in_root( + root, + ["local", "do", "update-mysql-authentication-plugin", "openedx"], + ) + dc_args, _dc_kwargs = mock_docker_compose.call_args + + self.assertIsNone(result.exception) + self.assertEqual(0, result.exit_code) + self.assertIn("lms-job", dc_args) + self.assertIn("caching_sha2_password", dc_args[-1]) + self.assertIn("openedx", dc_args[-1]) diff --git a/tutor/commands/jobs.py b/tutor/commands/jobs.py index 3937c6be07..b8aacd16ab 100644 --- a/tutor/commands/jobs.py +++ b/tutor/commands/jobs.py @@ -317,12 +317,15 @@ def sqlshell(args: list[str]) -> t.Iterable[tuple[str, str]]: yield ("lms", command) -@click.command(context_settings={"ignore_unknown_options": True}) +@click.command( + short_help="Update the authentication plugin of mysql users to caching_sha2_password.", + help=( + "Update the authentication plugin of mysql users to caching_sha2_password from mysql_native_password. You can specify either specific users to update or all to update all users." + ), +) @click.argument( "users", - is_flag=False, nargs=-1, - help="Update the authentication plugin of one or more users. Specify 'all' to update all users at once.", ) @click.pass_obj def update_mysql_authentication_plugin( @@ -342,12 +345,18 @@ def update_mysql_authentication_plugin( ) return - update_all = "all" in users + if not users: + fmt.echo_error( + f"Please specify a list of users to update the authentication plugin of.\n" + f"Or, specify 'all' to update all database users." + ) + return - users_to_update = list(plugins.iter_loaded()) if update_all else users.split(",") + update_all = "all" in users + users_to_update = list(plugins.iter_loaded()) if update_all else users query = get_mysql_change_authentication_plugin_query( - config, users_to_update, not users + config, users_to_update, update_all ) # In case there is no user to update the authentication plugin of @@ -355,7 +364,7 @@ def update_mysql_authentication_plugin( return mysql_command = ( - "mysql --user={{ MYSQL_ROOT_USERNAME }} --password={{ MYSQL_ROOT_PASSWORD }} --host={{ MYSQL_HOST }} --port={{ MYSQL_PORT }} --database={{ OPENEDX_MYSQL_DATABASE }} " + "mysql --user={{ MYSQL_ROOT_USERNAME }} --password={{ MYSQL_ROOT_PASSWORD }} --host={{ MYSQL_HOST }} --port={{ MYSQL_PORT }} --database={{ OPENEDX_MYSQL_DATABASE }} --show-warnings " + shlex.join(["-e", query]) ) diff --git a/tutor/commands/jobs_utils.py b/tutor/commands/jobs_utils.py index 8a4070ee4f..f829340caf 100644 --- a/tutor/commands/jobs_utils.py +++ b/tutor/commands/jobs_utils.py @@ -5,14 +5,14 @@ - `get_mysql_change_authentication_plugin_query`: Generates MySQL queries to update the authentication plugin for MySQL users. """ -from typing import List +from typing import Sequence -from tutor import exceptions, fmt +from tutor import fmt from tutor.types import Config, ConfigValue def get_mysql_change_authentication_plugin_query( - config: Config, users: List[str], all_users: bool + config: Config, users: Sequence[str], all_users: bool ) -> str: """ Generates MySQL queries to update the authentication plugin for MySQL users. @@ -44,9 +44,9 @@ def generate_mysql_authentication_plugin_update_query( fmt.echo_info( f"Authentication plugin of user {username} will be updated to caching_sha2_password" ) - return f"ALTER USER '{username}'@'{host}' IDENTIFIED with caching_sha2_password BY '{password}';" + return f"ALTER USER IF EXISTS '{username}'@'{host}' IDENTIFIED with caching_sha2_password BY '{password}';" - def generate_user_queries(users: List[str]) -> str: + def generate_user_queries(users: Sequence[str]) -> str: query = "" for user in users: user_uppercase = user.upper() @@ -54,7 +54,7 @@ def generate_user_queries(users: List[str]) -> str: f"{user_uppercase}_MYSQL_USERNAME" in config and f"{user_uppercase}_MYSQL_PASSWORD" in config ): - fmt.echo_warning( + fmt.echo_alert( f"Username or Password for User {user} not found in config. Skipping update process for User {user}." ) continue diff --git a/tutor/fmt.py b/tutor/fmt.py index 9cffdadd97..8af0592071 100644 --- a/tutor/fmt.py +++ b/tutor/fmt.py @@ -46,14 +46,6 @@ def alert(text: str) -> str: return click.style("⚠️ " + text, fg="yellow", bold=True) -def warning(text: str) -> str: - return click.style(text, fg="bright_yellow") - - -def echo_warning(text: str) -> None: - echo(warning(text)) - - def echo(text: str, err: bool = False) -> None: if os.environ.get("_TUTOR_COMPLETE"): if os.environ.get("COMP_WORDS") or os.environ.get("COMP_CWORD"):