6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-12-12 22:27:47 +00:00

refactor: move upgrade code to separate modules

This results in clearer code.
This commit is contained in:
Régis Behmo 2022-01-07 17:58:01 +01:00 committed by Régis Behmo
parent 9fc928a711
commit c61accedfc
5 changed files with 295 additions and 270 deletions

View File

@ -4,12 +4,13 @@ from typing import Any, List, Optional, Type
import click import click
from .. import config as tutor_config from tutor import config as tutor_config
from .. import env as tutor_env from tutor import env as tutor_env
from .. import exceptions, fmt, jobs, serialize, utils from tutor import exceptions, fmt, jobs, serialize, utils
from ..types import Config, get_typed from tutor.commands.config import save as config_save_command
from .config import save as config_save_command from tutor.commands.context import Context
from .context import Context from tutor.commands.upgrade.k8s import upgrade_from
from tutor.types import Config, get_typed
class K8sClients: class K8sClients:
@ -452,116 +453,7 @@ def wait(context: Context, name: str) -> None:
@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively") @click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively")
@click.pass_obj @click.pass_obj
def upgrade(context: Context, from_version: str, non_interactive: bool) -> None: def upgrade(context: Context, from_version: str, non_interactive: bool) -> None:
config = tutor_config.load(context.root) upgrade_from(context, from_version, interactive=not non_interactive)
running_version = from_version
if running_version == "ironwood":
upgrade_from_ironwood(config)
running_version = "juniper"
if running_version == "juniper":
upgrade_from_juniper(config)
running_version = "koa"
if running_version == "koa":
upgrade_from_koa(config)
running_version = "lilac"
if running_version == "lilac":
upgrade_from_lilac(config)
running_version = "maple"
# Update env such that the build environment is up-to-date
tutor_env.save(context.root, config)
if not non_interactive:
question = f"""
Your platform was successfuly upgraded from {from_version} to {running_version}.
Depending on your setup, you might have to rebuild some of your Docker images
and push them to your private registry (if any). You can do this now by running
the following command in a different shell:
tutor images build openedx # add your custom images here
tutor images push openedx
Press enter when you are ready to continue"""
click.confirm(
fmt.question(question), default=True, abort=True, prompt_suffix=" "
)
def upgrade_from_ironwood(config: Config) -> None:
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v3.6. There is "
"nothing left to do to upgrade from Ironwood."
)
return
message = """Automatic release upgrade is unsupported in Kubernetes. To upgrade from Ironwood, you should upgrade
your MongoDb cluster from v3.2 to v3.6. You should run something similar to:
# Upgrade from v3.2 to v3.4
tutor k8s stop
tutor config save --set DOCKER_IMAGE_MONGODB=mongo:3.4.24
tutor k8s start
tutor k8s exec mongodb mongo --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "3.4" })'
# Upgrade from v3.4 to v3.6
tutor k8s stop
tutor config save --set DOCKER_IMAGE_MONGODB=mongo:3.6.18
tutor k8s start
tutor k8s exec mongodb mongo --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "3.6" })'
tutor config save --unset DOCKER_IMAGE_MONGODB"""
fmt.echo_info(message)
def upgrade_from_juniper(config: Config) -> None:
if not config["RUN_MYSQL"]:
fmt.echo_info(
"You are not running MySQL (RUN_MYSQL=false). It is your "
"responsibility to upgrade your MySQL instance to v5.7. There is "
"nothing left to do to upgrade from Juniper."
)
return
message = """Automatic release upgrade is unsupported in Kubernetes. To upgrade from Juniper, you should upgrade
your MySQL database from v5.6 to v5.7. You should run something similar to:
tutor k8s start
tutor k8s exec mysql bash -e -c "mysql_upgrade \
-u $(tutor config printvalue MYSQL_ROOT_USERNAME) \
--password='$(tutor config printvalue MYSQL_ROOT_PASSWORD)'
"""
fmt.echo_info(message)
def upgrade_from_koa(config: Config) -> None:
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v4.0. There is "
"nothing left to do to upgrade to Lilac from Koa."
)
return
message = """Automatic release upgrade is unsupported in Kubernetes. To upgrade from Koa to Lilac, you should upgrade
your MongoDb cluster from v3.6 to v4.0. You should run something similar to:
tutor k8s stop
tutor config save --set DOCKER_IMAGE_MONGODB=mongo:4.0.25
tutor k8s start
tutor k8s exec mongodb mongo --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "4.0" })'
tutor config save --unset DOCKER_IMAGE_MONGODB
"""
fmt.echo_info(message)
def upgrade_from_lilac(config: Config) -> None:
fmt.echo_info(
"All Kubernetes services and deployments need to be deleted during "
"upgrade from Lilac to Maple"
)
delete_resources(config, resources=["deployments", "services"])
def kubectl_exec( def kubectl_exec(

View File

@ -1,13 +1,12 @@
from time import sleep
import click import click
from .. import config as tutor_config from tutor import config as tutor_config
from .. import env as tutor_env from tutor import env as tutor_env
from .. import exceptions, fmt, utils from tutor import exceptions, fmt, utils
from ..types import Config, get_typed from tutor.commands import compose
from . import compose from tutor.commands.config import save as config_save_command
from .config import save as config_save_command from tutor.commands.upgrade.local import upgrade_from
from tutor.types import Config, get_typed
class LocalJobRunner(compose.ComposeJobRunner): class LocalJobRunner(compose.ComposeJobRunner):
@ -49,10 +48,12 @@ def quickstart(context: click.Context, non_interactive: bool, pullimages: bool)
except exceptions.TutorError as e: except exceptions.TutorError as e:
fmt.echo_alert( fmt.echo_alert(
f"""Could not verify sufficient RAM allocation in Docker: f"""Could not verify sufficient RAM allocation in Docker:
{e} {e}
Tutor may not work if Docker is configured with < 4 GB RAM. Please follow the instructions from:
https://docs.tutor.overhang.io/install.html Tutor may not work if Docker is configured with < 4 GB RAM. Please follow instructions from:
"""
https://docs.tutor.overhang.io/install.html"""
) )
if tutor_env.needs_major_upgrade(context.obj.root): if tutor_env.needs_major_upgrade(context.obj.root):
@ -64,12 +65,7 @@ Tutor may not work if Docker is configured with < 4 GB RAM. Please follow the in
) )
click.echo(fmt.title("Interactive platform configuration")) click.echo(fmt.title("Interactive platform configuration"))
context.invoke( context.invoke(config_save_command, interactive=(not non_interactive))
config_save_command,
interactive=(not non_interactive),
set_vars=[],
unset_vars=[],
)
click.echo(fmt.title("Stopping any existing platform")) click.echo(fmt.title("Stopping any existing platform"))
context.invoke(compose.stop) context.invoke(compose.stop)
if pullimages: if pullimages:
@ -105,143 +101,7 @@ Your Open edX platform is ready and can be accessed at the following urls:
@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively") @click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively")
@click.pass_context @click.pass_context
def upgrade(context: click.Context, from_version: str, non_interactive: bool) -> None: def upgrade(context: click.Context, from_version: str, non_interactive: bool) -> None:
config = tutor_config.load(context.obj.root) upgrade_from(context, from_version, not non_interactive)
if not non_interactive:
question = """You are about to upgrade your Open edX platform. It is strongly recommended to make a backup before upgrading. To do so, run:
tutor local stop
sudo rsync -avr "$(tutor config printroot)"/ /tmp/tutor-backup/
In case of problem, to restore your backup you will then have to run: sudo rsync -avr /tmp/tutor-backup/ "$(tutor config printroot)"/
Are you sure you want to continue?"""
click.confirm(
fmt.question(question), default=True, abort=True, prompt_suffix=" "
)
running_version = from_version
if running_version == "ironwood":
upgrade_from_ironwood(context, config)
running_version = "juniper"
if running_version == "juniper":
upgrade_from_juniper(context, config)
running_version = "koa"
if running_version == "koa":
upgrade_from_koa(context, config)
running_version = "lilac"
if running_version == "lilac":
# Nothing to do here
running_version = "maple"
# Update env such that the build environment is up-to-date
tutor_env.save(context.obj.root, config)
if not non_interactive:
question = f"""
Your platform was successfuly upgraded from {from_version} to {running_version}.
Depending on your setup, you might have to rebuild some of your Docker images.
You can do this now by running the following command in a different shell:
tutor images build openedx # add your custom images here
Press enter when you are ready to continue"""
click.confirm(
fmt.question(question), default=True, abort=True, prompt_suffix=" "
)
def upgrade_from_ironwood(context: click.Context, config: Config) -> None:
click.echo(fmt.title("Upgrading from Ironwood"))
tutor_env.save(context.obj.root, config)
click.echo(fmt.title("Stopping any existing platform"))
context.invoke(compose.stop)
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v3.6. There is "
"nothing left to do to upgrade from Ironwood to Juniper."
)
return
upgrade_mongodb(context, config, "3.4", "3.4")
context.invoke(compose.stop)
upgrade_mongodb(context, config, "3.6", "3.6")
context.invoke(compose.stop)
def upgrade_from_juniper(context: click.Context, config: Config) -> None:
click.echo(fmt.title("Upgrading from Juniper"))
tutor_env.save(context.obj.root, config)
click.echo(fmt.title("Stopping any existing platform"))
context.invoke(compose.stop)
if not config["RUN_MYSQL"]:
fmt.echo_info(
"You are not running MySQL (RUN_MYSQL=false). It is your "
"responsibility to upgrade your MySQL instance to v5.7. There is "
"nothing left to do to upgrade from Juniper."
)
return
click.echo(fmt.title("Upgrading MySQL from v5.6 to v5.7"))
context.invoke(compose.start, detach=True, services=["mysql"])
context.invoke(
compose.execute,
args=[
"mysql",
"bash",
"-e",
"-c",
"mysql_upgrade -u {} --password='{}'".format(
config["MYSQL_ROOT_USERNAME"], config["MYSQL_ROOT_PASSWORD"]
),
],
)
context.invoke(compose.stop)
def upgrade_from_koa(context: click.Context, config: Config) -> None:
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v4.0. There is "
"nothing left to do to upgrade from Koa to Lilac."
)
return
upgrade_mongodb(context, config, "4.0.25", "4.0")
def upgrade_mongodb(
context: click.Context,
config: Config,
to_docker_version: str,
to_compatibility_version: str,
) -> None:
click.echo(fmt.title("Upgrading MongoDb to v{}".format(to_docker_version)))
# Note that the DOCKER_IMAGE_MONGODB value is never saved, because we only save the
# environment, not the configuration.
config["DOCKER_IMAGE_MONGODB"] = "mongo:{}".format(to_docker_version)
tutor_env.save(context.obj.root, config)
context.invoke(compose.start, detach=True, services=["mongodb"])
fmt.echo_info("Waiting for mongodb to boot...")
sleep(10)
context.invoke(
compose.execute,
args=[
"mongodb",
"mongo",
"--eval",
'db.adminCommand({ setFeatureCompatibilityVersion: "%s" })'
% to_compatibility_version,
],
)
context.invoke(compose.stop)
local.add_command(quickstart) local.add_command(quickstart)

View File

View File

@ -0,0 +1,123 @@
import click
from tutor import config as tutor_config
from tutor import env as tutor_env
from tutor import fmt
from tutor.commands import k8s
from tutor.commands.context import Context
from tutor.types import Config
def upgrade_from(
context: Context, from_version: str, interactive: bool = False
) -> None:
config = tutor_config.load(context.root)
running_version = from_version
if running_version == "ironwood":
upgrade_from_ironwood(config)
running_version = "juniper"
if running_version == "juniper":
upgrade_from_juniper(config)
running_version = "koa"
if running_version == "koa":
upgrade_from_koa(config)
running_version = "lilac"
if running_version == "lilac":
upgrade_from_lilac(config)
running_version = "maple"
# Update env such that the build environment is up-to-date
tutor_env.save(context.root, config)
if interactive:
question = f"""Your platform was successfuly upgraded from {from_version} to {running_version}.
Depending on your setup, you might have to rebuild some of your Docker images
and push them to your private registry (if any). You can do this now by running
the following command in a different shell:
tutor images build openedx # add your custom images here
tutor images push openedx
Press enter when you are ready to continue"""
click.confirm(
fmt.question(question), default=True, abort=True, prompt_suffix=" "
)
def upgrade_from_ironwood(config: Config) -> None:
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v3.6. There is "
"nothing left to do to upgrade from Ironwood."
)
return
message = """Automatic release upgrade is unsupported in Kubernetes. To upgrade from Ironwood, you should upgrade
your MongoDb cluster from v3.2 to v3.6. You should run something similar to:
# Upgrade from v3.2 to v3.4
tutor k8s stop
tutor config save --set DOCKER_IMAGE_MONGODB=mongo:3.4.24
tutor k8s start
tutor k8s exec mongodb mongo --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "3.4" })'
# Upgrade from v3.4 to v3.6
tutor k8s stop
tutor config save --set DOCKER_IMAGE_MONGODB=mongo:3.6.18
tutor k8s start
tutor k8s exec mongodb mongo --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "3.6" })'
tutor config save --unset DOCKER_IMAGE_MONGODB"""
fmt.echo_info(message)
def upgrade_from_juniper(config: Config) -> None:
if not config["RUN_MYSQL"]:
fmt.echo_info(
"You are not running MySQL (RUN_MYSQL=false). It is your "
"responsibility to upgrade your MySQL instance to v5.7. There is "
"nothing left to do to upgrade from Juniper."
)
return
message = """Automatic release upgrade is unsupported in Kubernetes. To upgrade from Juniper, you should upgrade
your MySQL database from v5.6 to v5.7. You should run something similar to:
tutor k8s start
tutor k8s exec mysql bash -e -c "mysql_upgrade \
-u $(tutor config printvalue MYSQL_ROOT_USERNAME) \
--password='$(tutor config printvalue MYSQL_ROOT_PASSWORD)'
"""
fmt.echo_info(message)
def upgrade_from_koa(config: Config) -> None:
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v4.0. There is "
"nothing left to do to upgrade to Lilac from Koa."
)
return
message = """Automatic release upgrade is unsupported in Kubernetes. To upgrade from Koa to Lilac, you should upgrade
your MongoDb cluster from v3.6 to v4.0. You should run something similar to:
tutor k8s stop
tutor config save --set DOCKER_IMAGE_MONGODB=mongo:4.0.25
tutor k8s start
tutor k8s exec mongodb mongo --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "4.0" })'
tutor config save --unset DOCKER_IMAGE_MONGODB
"""
fmt.echo_info(message)
def upgrade_from_lilac(config: Config) -> None:
fmt.echo_info(
"All Kubernetes services and deployments need to be deleted during "
"upgrade from Lilac to Maple"
)
k8s.delete_resources(config, resources=["deployments", "services"])

View File

@ -0,0 +1,150 @@
from time import sleep
import click
from tutor import config as tutor_config
from tutor import env as tutor_env
from tutor import fmt
from tutor.commands import compose
from tutor.types import Config
def upgrade_from(
context: click.Context, from_version: str, interactive: bool = False
) -> None:
config = tutor_config.load(context.obj.root)
if interactive:
question = """You are about to upgrade your Open edX platform.
It is strongly recommended to make a backup before upgrading. To do so, run:
tutor local stop
sudo rsync -avr "$(tutor config printroot)"/ /tmp/tutor-backup/
In case of problem, to restore your backup you will then have to run: sudo rsync -avr /tmp/tutor-backup/ "$(tutor config printroot)"/
Are you sure you want to continue?"""
click.confirm(
fmt.question(question), default=True, abort=True, prompt_suffix=" "
)
running_version = from_version
if running_version == "ironwood":
upgrade_from_ironwood(context, config)
running_version = "juniper"
if running_version == "juniper":
upgrade_from_juniper(context, config)
running_version = "koa"
if running_version == "koa":
upgrade_from_koa(context, config)
running_version = "lilac"
if running_version == "lilac":
# Nothing to do here
running_version = "maple"
# Update env such that the build environment is up-to-date
tutor_env.save(context.obj.root, config)
if interactive:
question = f"""Your platform was successfuly upgraded from {from_version} to {running_version}.
Depending on your setup, you might have to rebuild some of your Docker images.
You can do this now by running the following command in a different shell:
tutor images build openedx # add your custom images here
Press enter when you are ready to continue"""
click.confirm(
fmt.question(question), default=True, abort=True, prompt_suffix=" "
)
def upgrade_from_ironwood(context: click.Context, config: Config) -> None:
click.echo(fmt.title("Upgrading from Ironwood"))
tutor_env.save(context.obj.root, config)
click.echo(fmt.title("Stopping any existing platform"))
context.invoke(compose.stop)
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v3.6. There is "
"nothing left to do to upgrade from Ironwood to Juniper."
)
return
upgrade_mongodb(context, config, "3.4", "3.4")
context.invoke(compose.stop)
upgrade_mongodb(context, config, "3.6", "3.6")
context.invoke(compose.stop)
def upgrade_from_juniper(context: click.Context, config: Config) -> None:
click.echo(fmt.title("Upgrading from Juniper"))
tutor_env.save(context.obj.root, config)
click.echo(fmt.title("Stopping any existing platform"))
context.invoke(compose.stop)
if not config["RUN_MYSQL"]:
fmt.echo_info(
"You are not running MySQL (RUN_MYSQL=false). It is your "
"responsibility to upgrade your MySQL instance to v5.7. There is "
"nothing left to do to upgrade from Juniper."
)
return
click.echo(fmt.title("Upgrading MySQL from v5.6 to v5.7"))
context.invoke(compose.start, detach=True, services=["mysql"])
context.invoke(
compose.execute,
args=[
"mysql",
"bash",
"-e",
"-c",
f"mysql_upgrade -u {config['MYSQL_ROOT_USERNAME']} --password='{config['MYSQL_ROOT_PASSWORD']}'",
],
)
context.invoke(compose.stop)
def upgrade_from_koa(context: click.Context, config: Config) -> None:
if not config["RUN_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (RUN_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v4.0. There is "
"nothing left to do to upgrade from Koa to Lilac."
)
return
upgrade_mongodb(context, config, "4.0.25", "4.0")
def upgrade_mongodb(
context: click.Context,
config: Config,
to_docker_version: str,
to_compatibility_version: str,
) -> None:
click.echo(fmt.title(f"Upgrading MongoDb to v{to_docker_version}"))
# Note that the DOCKER_IMAGE_MONGODB value is never saved, because we only save the
# environment, not the configuration.
config["DOCKER_IMAGE_MONGODB"] = f"mongo:{to_docker_version}"
tutor_env.save(context.obj.root, config)
context.invoke(compose.start, detach=True, services=["mongodb"])
fmt.echo_info("Waiting for mongodb to boot...")
sleep(10)
context.invoke(
compose.execute,
args=[
"mongodb",
"mongo",
"--eval",
f'db.adminCommand({{ setFeatureCompatibilityVersion: "{to_compatibility_version}" }})',
],
)
context.invoke(compose.stop)