-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add do
command to update the authentication plugin of MySQL users
#1097
base: main
Are you sure you want to change the base?
feat: add do
command to update the authentication plugin of MySQL users
#1097
Conversation
tutor/commands/upgrade/common.py
Outdated
@@ -43,6 +43,39 @@ def upgrade_from_nutmeg(context: click.Context, config: Config) -> None: | |||
) | |||
|
|||
|
|||
def get_mysql_change_authentication_plugin_query(config: Config) -> str: | |||
"""Helper function to generate queries depending on the loaded plugins""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the docstring can be upgraded to highlight what is done by default and what is done based on plugin's availability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
tutor/commands/upgrade/k8s.py
Outdated
# We need to first revert MySQL back to v8.1 to build the data dictionary on it | ||
# And also to update the authentication plugin as it is disabled on v8.4 | ||
message = f"""Automatic release upgrade is unsupported in Kubernetes. If you are upgrading from Olive or an earlier release to Redwood, you | ||
should upgrade the authentication plugin of your users. To upgrade, run something similar to: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of saying run something similar, we should add the exact commands the user should run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the exact commands that the user should run. I've updated the wording in the message.
changelog.d/20240718_171945_danyal.faheem_mysql_authentication_plugin_change.md
Outdated
Show resolved
Hide resolved
tutor/commands/upgrade/common.py
Outdated
@@ -43,6 +43,46 @@ def upgrade_from_nutmeg(context: click.Context, config: Config) -> None: | |||
) | |||
|
|||
|
|||
def get_mysql_change_authentication_plugin_query(config: Config) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we should have an override/additional param provided by user to only upgrade a particular plugin's database. Lets say they have upgraded the default already and do not have plugins enabled. Once plugins are enabled, they will be re-doing the migration for default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I was also thinking that we could add a --users
option with comma separated name of users to the do command. This way, users can also choose to upgrade their choice of databases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a --users
option as well.
tutor/commands/upgrade/common.py
Outdated
) -> str: | ||
return f"ALTER USER '{username}'@'{host}' IDENTIFIED with caching_sha2_password BY '{password}';" | ||
|
||
host = "%" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the host set to %?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The host for users created by Tutor is always set to '%'. The users created by MySQL itself uses the 'localhost' host.
adaa02d
to
820cebe
Compare
ef6022d
to
4d0042c
Compare
@@ -44,7 +44,11 @@ services: | |||
--character-set-server=utf8mb4 | |||
--collation-server=utf8mb4_unicode_ci | |||
--binlog-expire-logs-seconds=259200 | |||
# We only require this option for MySQL 8.4 and above | |||
# Breaks MySQL for previous versions as this option does not exist on versions earlier than 8.4 | |||
{% if DOCKER_IMAGE_MYSQL.split(':')[-1] == "latest" or (DOCKER_IMAGE_MYSQL.split(':')[-1].split('.') | map('int') | list >= '8.4.0'.split('.') | map('int') | list) -%} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we can set DOCKER_IMAGE_MYSQL.split(':')[-1] to a variable and use that in comparison. It will make the code readable.
docs/local.rst
Outdated
|
||
tutor local do update_mysql_authentication_plugin --users=discovery,ecommerce | ||
|
||
Do note that if you are updating a specific user, there should be corresponding entries in the configuration for the mysql username and password for that user. For example, if you are trying to update the user ``myuser``, the following case sensitive entries need to be present in the configuration:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this paragraph can be reworded to focus that for any particular user, the command expects the configuration settings in a certain format. Then, the myuser examples can be added afterwards/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated this paragraph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While testing, I came across the following error
Error: Username or Password for User mfe not found in config. Please make sure that the following entries are present in the configuration:
MFE_MYSQL_USERNAME
MFE_MYSQL_PASSWORD
The current code raises the exception if config is not found. It is iterating on all the plugins. Not all plugins would have DB users. Instead of raising, we can log warning and continue.
do
command to update the authentication plugin of MySQL users
Hello @regisb, can u have a look at it for it's final iteration? |
tutor/fmt.py
Outdated
@@ -46,6 +46,14 @@ def alert(text: str) -> str: | |||
return click.style("⚠️ " + text, fg="yellow", bold=True) | |||
|
|||
|
|||
def warning(text: str) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of warning
when we already have alert
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alert prints to standard error and this seemed like a situation where we just wanted to display a warning rather than an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep an alert. We don't want to write to stdout because it might mangle output from other commands, such as tutor config printvalue/printroot
. And if we write to stderr than we are better off just using alert.
tutor/commands/jobs.py
Outdated
@@ -315,6 +317,49 @@ def sqlshell(args: list[str]) -> t.Iterable[tuple[str, str]]: | |||
yield ("lms", command) | |||
|
|||
|
|||
@click.command(context_settings={"ignore_unknown_options": True}) | |||
@click.option( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be an option, but an argument, because it's mandatory. The command signature should not be:
tutor local do update_mysql_authentication_plugin --users=regis,danyal
but:
tutor local do update_mysql_authentication_plugin regis danyal
Moreover, we should be able to update all users with users=all:
tutor local do update_mysql_authentication_plugin all
tutor/commands/jobs_utils.py
Outdated
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}';" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is extremely confusing, and there are many ways in which it can fail. In order to improve it, I'd like to understand if it's possible to automatically detect all mysql users that were created with the legacy authentication plugin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only way to do that would be to get the output of a MySQL query inside tutor codebase. I am not aware of any method that we can use to get the output of a command run inside a container inside the tutor codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming we could get the output of a mysql query in tutor, what would be that query?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something similar to this:
select user from mysql.user where plugin='mysql_native_password' and host='%';
We need to limit the host to '%' as Tutor uses that host value by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Regis, any thoughts on how we should proceed with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure. basically, we need to perform all tasks inside a single templated bash script -- including the mysql --user...
part. This bash script can be templated, such that we don't have to pass config values to the script-generating function. I'm sorry I don't have time to think about this more...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So maybe something similar to a stored procedure? Something akin to what we did in #1079?
We could get the names of all the users and update them inside that procedure. Although, I'd have to look into how we would manage passwords then.
c872809
to
458c6de
Compare
…rs to caching_sha2_password
458c6de
to
98d6753
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: once merged and rebase to sumac, we can add a "warning" in upgrade that users should do the auth plugin updates to ensure future upgrades of MySQL do not break their systems.
user_uppercase = user.upper() | ||
if not ( | ||
f"{user_uppercase}_MYSQL_USERNAME" in config | ||
and f"{user_uppercase}_MYSQL_PASSWORD" in config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very fragile. We can't rely on the arbitrary naming of some configuration settings to auto-detect user names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We found that this was a convention used across the plugins, but I can understand your position here. However, we still need the exact username and password to perform this operation. Therefore, I propose that we can provide an option to provide the exact username and password. Something similar to:
tutor local do update-mysql-authentication-plugin discovery ecommerce --usernames discovery-username,ecommerce-username --passwords discovery-password,ecommerce-password
This isn't very intuitive but since this would be a one time operation, maybe it is fine? Another con I see here is that users will have to have passwords out in the open for headless scripts unless they utilize environment variables.
I am open to any suggestions you might have.
closes #1095
This PR is a continuation of the issue mentioned in #1089.
all
to update all users or specific users to update the authentication plugin of.USER_MYSQL_USERNAME
andUSER_MYSQL_PASSWORD
to work.