Skip to content
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

Update MySQL #207

Merged
merged 6 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions installation_and_upgrade/ibex_install_utils/admin_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import os
import tempfile
from time import sleep
from typing import Any, Generator


class AdminRunner:
@staticmethod
def run_command(command, parameters, expected_return_val=0):
def run_command(command: str, parameters: str, expected_return_val: int | None = 0) -> None:
try:
import win32api
import win32con
Expand Down Expand Up @@ -40,13 +41,15 @@ class AdminCommandBuilder:
Builder for running multiple commands sequentially as admin.
"""

def __init__(self):
self._commands = []
def __init__(self) -> None:
self._commands: list[tuple[str, str, int | None]] = []

def add_command(self, command, parameters, expected_return_val=0):
def add_command(
self, command: str, parameters: str, expected_return_val: int | None = 0
) -> None:
self._commands.append((command, parameters, expected_return_val))

def run_all(self):
def run_all(self) -> str:
bat_file = ""

log_file = tempfile.NamedTemporaryFile(mode="w+t", suffix=".log", delete=False)
Expand All @@ -63,7 +66,8 @@ def run_all(self):

with temp_bat_file(bat_file) as f:
print(
f"Executing bat script as admin. Saved as {f}. Check for an admin prompt. Log at {log_file.name}."
f"Executing bat script as admin. Saved as {f}. Check for an admin prompt. "
f"Log at {log_file.name}."
)
sleep(1) # Wait for file handle to be closed etc
try:
Expand All @@ -80,9 +84,9 @@ def run_all(self):


@contextlib.contextmanager
def temp_bat_file(contents):
def temp_bat_file(contents: str) -> Generator[str, None, Any]:
f = tempfile.NamedTemporaryFile(mode="w+t", suffix=".bat", delete=False)
try:
f = tempfile.NamedTemporaryFile(mode="w+t", suffix=".bat", delete=False)
f.write(contents)
f.close()
yield f.name
Expand Down
52 changes: 29 additions & 23 deletions installation_and_upgrade/ibex_install_utils/tasks/mysql_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess
import zipfile
from time import sleep
from typing import Generator

from ibex_install_utils.admin_runner import AdminCommandBuilder
from ibex_install_utils.exceptions import ErrorInRun
Expand All @@ -27,15 +28,11 @@
# For Py2 compatibility, can be removed once we are on Py3.
DETACHED_PROCESS = 0x00000008

try:
from contextlib import closing
except ImportError:
from contextlib2 import closing

from contextlib import closing

MYSQL8_INSTALL_DIR = os.path.join(APPS_BASE_DIR, "MySQL")
MYSQL57_INSTALL_DIR = os.path.join("C:\\", "Program Files", "MySQL", "MySQL Server 5.7")
MYSQL_LATEST_VERSION = "8.0.39"
MYSQL_LATEST_VERSION = "8.4.2"
MYSQL_ZIP = os.path.join(
INST_SHARE_AREA,
"kits$",
Expand Down Expand Up @@ -129,9 +126,10 @@ def _configure_mysql(self) -> None:
# Restart to pick up new my.ini
admin_commands = AdminCommandBuilder()
admin_commands.add_command("sc", "stop MYSQL80", expected_return_val=None)
admin_commands.add_command("sc", "stop MYSQL84", expected_return_val=None)
# Sleep to wait for service to stop so we can restart it.
admin_commands.add_command("ping", "-n 10 127.0.0.1 >nul", expected_return_val=None)
admin_commands.add_command("sc", "start MYSQL80", expected_return_val=None)
admin_commands.add_command("sc", "start MYSQL84", expected_return_val=None)
admin_commands.run_all()

def _remove_old_versions_of_mysql8(self, clean_install: bool) -> None:
Expand All @@ -144,6 +142,8 @@ def _remove_old_versions_of_mysql8(self, clean_install: bool) -> None:
admin_commands = AdminCommandBuilder()
admin_commands.add_command("sc", "stop MYSQL80", expected_return_val=None)
admin_commands.add_command("sc", "delete MYSQL80", expected_return_val=None)
admin_commands.add_command("sc", "stop MYSQL84", expected_return_val=None)
admin_commands.add_command("sc", "delete MYSQL84", expected_return_val=None)
admin_commands.run_all()

sleep(5) # Time for service to stop
Expand Down Expand Up @@ -190,7 +190,7 @@ def _initialize_mysql_data_area_for_vhd(self) -> None:
).run()

@contextlib.contextmanager
def temporarily_run_mysql(self, sql_password: str) -> None:
def temporarily_run_mysql(self, sql_password: str) -> Generator[None, None, None]:
mysqld = os.path.join(MYSQL8_INSTALL_DIR, "bin", "mysqld.exe")

# spawn service in background
Expand All @@ -215,13 +215,18 @@ def temporarily_run_mysql(self, sql_password: str) -> None:
).run()

def _setup_database_users_and_tables(self, vhd_install: bool = True) -> None:
sql_password = self.prompt.prompt(
"Enter the MySQL root password:",
UserPrompt.ANY,
os.getenv("MYSQL_PASSWORD", "environment variable not set"),
show_automatic_answer=False,
)

sql_password = ""
retry_count = 5
while --retry_count > 0:
sql_password = self.prompt.prompt(
"Enter the MySQL root password:",
UserPrompt.ANY,
os.getenv("MYSQL_PASSWORD", "environment variable not set"),
show_automatic_answer=False,
).strip()
if len(sql_password) > 0:
break
print("Please enter a non blank password")
if vhd_install:
# In the VHD install, need to explicitly temporarily run MySQL.
cm = self.temporarily_run_mysql(sql_password)
Expand All @@ -239,7 +244,7 @@ def _setup_database_users_and_tables(self, vhd_install: bool = True) -> None:
"root",
"-e",
"ALTER USER 'root'@'localhost' "
f"IDENTIFIED WITH mysql_native_password BY '{sql_password}';FLUSH "
f"IDENTIFIED WITH caching_sha2_password BY '{sql_password}';FLUSH "
"privileges; ",
],
log_command_args=False, # To make sure password doesn't appear in jenkins log.
Expand All @@ -260,17 +265,17 @@ def _setup_mysql8_service(self) -> None:
# Wait for initialize since admin runner can't wait for completion.
# Maybe we can detect completion another way?
admin_commands.add_command(
mysqld, '--install MYSQL80 --datadir="{}"'.format(os.path.join(MYSQL_FILES_DIR, "data"))
mysqld, '--install MYSQL84 --datadir="{}"'.format(os.path.join(MYSQL_FILES_DIR, "data"))
)

admin_commands.add_command("sc", "start MYSQL80", expected_return_val=None)
admin_commands.add_command("sc", "start MYSQL84", expected_return_val=None)
# we use "delayed-auto" for start= as we have some ibex installations
# where a required disk volume doesn't get mounted in time if just "auto" is used
admin_commands.add_command("sc", "config MYSQL80 start= delayed-auto")
admin_commands.add_command("sc", "config MYSQL84 start= delayed-auto")
admin_commands.add_command(
"sc", "failure MYSQL80 reset= 900 actions= restart/10000/restart/30000/restart/60000"
"sc", "failure MYSQL84 reset= 900 actions= restart/10000/restart/30000/restart/60000"
)
admin_commands.add_command("sc", "failureflag MYSQL80 1")
admin_commands.add_command("sc", "failureflag MYSQL84 1")
admin_commands.add_command(
"netsh", "advfirewall firewall delete rule name=mysqld.exe", None
) # remove old firewall rules
Expand All @@ -295,6 +300,7 @@ def _install_latest_mysql8(self, clean_install: bool) -> None:
self._create_mysql_binaries()

if clean_install:
shutil.rmtree(MYSQL_FILES_DIR)
os.makedirs(MYSQL_FILES_DIR)
mysqld = os.path.join(MYSQL8_INSTALL_DIR, "bin", "mysqld.exe")

Expand All @@ -321,7 +327,7 @@ def install_mysql_for_vhd(self) -> None:

Ensure we start from a clean slate. We are creating VHDs so
we can assume that no files should exist in
C:\instrument\apps\mysql or c:\instrument\var\mysql and
C:\\instrument\\apps\\mysql or c:\\instrument\\var\\mysql and
delete them if they do exist. This facilitates
developer testing/resuming the script if it failed halfway through
"""
Expand Down Expand Up @@ -386,7 +392,7 @@ def install_mysql(self, force: bool = False) -> None:
force = True
else:
return
clean_install = force
clean_install = force or MySQL().get_installed_version().startswith("8.0")
self._remove_old_versions_of_mysql8(clean_install=clean_install)

self._install_vcruntime140()
Expand Down