From d32b8f8b7cd13ce6e9e7c6f4a10d1821c56ea28c Mon Sep 17 00:00:00 2001 From: Danyal-Faheem Date: Mon, 27 Jan 2025 16:14:11 +0500 Subject: [PATCH] fix: updated command options and docs --- docs/local.rst | 10 +++--- docs/troubleshooting.rst | 6 ++-- tests/commands/test_jobs.py | 28 +++++++++++++--- tutor/commands/jobs.py | 64 ++++++++++++++++++++----------------- 4 files changed, 68 insertions(+), 40 deletions(-) diff --git a/docs/local.rst b/docs/local.rst index fc9ac51716..d79c4d0ba4 100644 --- a/docs/local.rst +++ b/docs/local.rst @@ -179,13 +179,15 @@ Tutor makes it easy do so with this handy command:: tutor local do update-mysql-authentication-plugin USERNAME -The password will be required to be entered interactively. Optionally, the password can also be provided as part of the command. To update the openedx mysql user:: +The password will not be required for official plugins that have database users as tutor can infer it from the config. If the password cannot be found by tutor, you will be prompted to enter the password interactively. Alternatively, the password can also be provided as an option:: - tutor local do update-mysql-authentication-plugin $(tutor config printvalue OPENEDX_MYSQL_USERNAME) --password=$(tutor config printvalue OPENEDX_MYSQL_PASSWORD) + tutor local do update-mysql-authentication-plugin USERNAME --password=PASSWORD -Tutor may prompt you with some warnings if the entered password is suspected to be wrong. To avoid these prompts, the non-interactive option should be used. To update the root mysql user non-interactively:: +To update the database users for a vanilla tutor installation:: + + tutor local do update-mysql-authentication-plugin $(tutor config printvalue OPENEDX_MYSQL_USERNAME) + tutor local do update-mysql-authentication-plugin $(tutor config printvalue MYSQL_ROOT_USERNAME) - tutor local do update-mysql-authentication-plugin $(tutor config printvalue MYSQL_ROOT_USERNAME) --password=$(tutor config printvalue MYSQL_ROOT_PASSWORD) --non-interactive Running arbitrary ``manage.py`` commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index bc6c10324d..8bf5a21f04 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -220,9 +220,9 @@ The detailed steps are mentioned in `tutor-mfe ` do command in tutor can be used to fix this issue. To update the database users for a vanilla tutor installation:: - tutor local do update-mysql-authentication-plugin $(tutor config printvalue OPENEDX_MYSQL_USERNAME) --password=$(tutor config printvalue OPENEDX_MYSQL_PASSWORD) --non-interactive - tutor local do update-mysql-authentication-plugin $(tutor config printvalue MYSQL_ROOT_USERNAME) --password=$(tutor config printvalue MYSQL_ROOT_PASSWORD) --non-interactive + tutor local do update-mysql-authentication-plugin $(tutor config printvalue OPENEDX_MYSQL_USERNAME) + tutor local do update-mysql-authentication-plugin $(tutor config printvalue MYSQL_ROOT_USERNAME) diff --git a/tests/commands/test_jobs.py b/tests/commands/test_jobs.py index 3ba635dd34..13081232a7 100644 --- a/tests/commands/test_jobs.py +++ b/tests/commands/test_jobs.py @@ -166,7 +166,7 @@ def test_convert_mysql_utf8mb4_charset_exclude_tables(self) -> None: self.assertIn("course", dc_args[-1]) self.assertIn("auth", dc_args[-1]) - def test_update_mysql_authentication_plugin(self) -> None: + def test_update_mysql_authentication_plugin_official_plugin(self) -> None: with temporary_root() as root: self.invoke_in_root(root, ["config", "save"]) with patch("tutor.utils.docker_compose") as mock_docker_compose: @@ -177,8 +177,6 @@ def test_update_mysql_authentication_plugin(self) -> None: "do", "update-mysql-authentication-plugin", "openedx", - "--password=password", - "--non-interactive", ], ) dc_args, _dc_kwargs = mock_docker_compose.call_args @@ -188,4 +186,26 @@ def test_update_mysql_authentication_plugin(self) -> None: self.assertIn("lms-job", dc_args) self.assertIn("caching_sha2_password", dc_args[-1]) self.assertIn("openedx", dc_args[-1]) - self.assertIn("password", dc_args[-1]) + + def test_update_mysql_authentication_plugin_custom_plugin(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", + "mypluginuser", + "--password=mypluginpassword", + ], + ) + 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("mypluginuser", dc_args[-1]) + self.assertIn("mypluginpassword", dc_args[-1]) diff --git a/tutor/commands/jobs.py b/tutor/commands/jobs.py index 2fdc0e2f25..1f51fdd49f 100644 --- a/tutor/commands/jobs.py +++ b/tutor/commands/jobs.py @@ -20,7 +20,6 @@ set_theme_template, ) from tutor.hooks import priorities -from tutor.types import get_typed class DoGroup(click.Group): @@ -387,17 +386,14 @@ def generate_query_to_append(tables: list[str], exclude: bool = False) -> str: @click.option( "-p", "--password", - help="Specify password from the command line. If undefined, you will be prompted to input a password", - prompt=True, - hide_input=True, + help="Specify password from the command line.", ) @click.argument( "user", ) -@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively") @click.pass_obj def update_mysql_authentication_plugin( - context: Context, user: str, password: str, non_interactive: bool + context: Context, user: str, password: str ) -> t.Iterable[tuple[str, str]]: """ Update the authentication plugin of MySQL users from mysql_native_password to caching_sha2_password @@ -413,30 +409,40 @@ def update_mysql_authentication_plugin( ) return - conventional_password_key = f"{user.upper()}_MYSQL_PASSWORD" + # Official plugins that have their own mysql user + known_plugins_with_mysql_keys = [ + "credentials", + "discovery", + "ecommerce", + "jupyter", + "notes", + "openedx", + "xqueue", + ] - # Prompt for confirmation to move forward if password not present in config with the conventional format USER_MYSQL_PASSWORD - if not non_interactive and not conventional_password_key in config: - if not click.confirm( - fmt.question( - f"""Password for user {user} could not be verified. The entered password is: {password} -Would you still like to continue with the upgrade process? Note: a wrong password would update the password for the user.""" - ) - ): - return - # Prompt for confirmation to move forward is password is present in config with the conventional format USER_MYSQL_PASSWORD - # but it is not the same as the value of that config variable - elif ( - not non_interactive - and get_typed(config, conventional_password_key, str, "") != password - ): - if not click.confirm( - fmt.question( - f"""Password for user {user} is suspected to be wrong. The entered password is: {password} while the password suspected to be the correct one is {config[conventional_password_key]} -Would you still like to continue with the upgrade process? Note: a wrong password would update the password for the user.""" - ) - ): - return + # Create a list of the usernames and passwords + known_mysql_credentials_keys = [ + (f"{plugin.upper()}_MYSQL_USERNAME", f"{plugin.upper()}_MYSQL_PASSWORD") + for plugin in known_plugins_with_mysql_keys + ] + # Add the root user as it is the only one that is different from the rest + known_mysql_credentials_keys.append(("MYSQL_ROOT_USERNAME", "MYSQL_ROOT_PASSWORD")) + + known_mysql_credentials = {} + # Build the dictionary of known credentials + for k, v in known_mysql_credentials_keys: + if username := config.get(k): + known_mysql_credentials[username] = config[v] + + if not password: + password = known_mysql_credentials.get(user) # type: ignore + + # Prompt the user if password was not found in config + if not password: + password = click.prompt( + f"Please enter the password for the user {user}", + type=str, + ) host = "%"