diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d27f33d3bb..de66cc32ed3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Note: Breaking changes between versions are indicated by "💥". ## Unreleased +- [Feature] Add `tutor dev quickstart` command, which is equivalent to `tutor local quickstart`, but uses dev containers instead of local production ones. This should remove some friction from the Open edX development setup process, which previously required that users provision using local producation containers (`tutor local quickstart`) but then stop them and switch to dev containers (`tutor local stop && tutor dev start -d`). + ## v13.1.8 (2022-03-18) - [Bugfix] Fix "evalsymlink failure" during `k8s quickstart` (#611). diff --git a/README.rst b/README.rst index d9e2407b4b7..90ca9deb69b 100644 --- a/README.rst +++ b/README.rst @@ -64,7 +64,9 @@ Quickstart ---------- 1. Install the `latest stable release `_ of Tutor -2. Run ``tutor local quickstart`` +2. Run one of these commands: + * ``tutor local quickstart`` (for production deployment) + * ``tutor dev quickstart`` (for development) 3. You're done! Documentation diff --git a/docs/configuration.rst b/docs/configuration.rst index 54917a9bae6..fcff830c53d 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -15,7 +15,7 @@ This section does not cover :ref:`plugin development `. For simple chan Configuration ------------- -With Tutor, all Open edX deployment parameters are stored in a single ``config.yml`` file. This is the file that is generated when you run ``tutor local quickstart`` or ``tutor config save``. To view the content of this file, run:: +With Tutor, all Open edX deployment parameters are stored in a single ``config.yml`` file. This is the file that is generated when you run ``tutor ... quickstart`` or ``tutor config save``. To view the content of this file, run:: cat "$(tutor config printroot)/config.yml" diff --git a/tests/commands/test_compose.py b/tests/commands/test_compose.py new file mode 100644 index 00000000000..cac0e599282 --- /dev/null +++ b/tests/commands/test_compose.py @@ -0,0 +1,19 @@ +import unittest + +from click.testing import CliRunner + +from tutor.commands.compose import quickstart, upgrade + + +class ComposeTests(unittest.TestCase): + def test_compose_quickstart_help(self) -> None: + runner = CliRunner() + result = runner.invoke(quickstart, ["--help"]) + self.assertEqual(0, result.exit_code) + self.assertIsNone(result.exception) + + def test_compose_upgrade_help(self) -> None: + runner = CliRunner() + result = runner.invoke(upgrade, ["--help"]) + self.assertEqual(0, result.exit_code) + self.assertIsNone(result.exception) diff --git a/tests/commands/test_local.py b/tests/commands/test_local.py index 88d930d2fac..96fbec423c2 100644 --- a/tests/commands/test_local.py +++ b/tests/commands/test_local.py @@ -2,7 +2,7 @@ from click.testing import CliRunner -from tutor.commands.local import local, quickstart, upgrade +from tutor.commands.local import local class LocalTests(unittest.TestCase): @@ -11,15 +11,3 @@ def test_local_help(self) -> None: result = runner.invoke(local, ["--help"]) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) - - def test_local_quickstart_help(self) -> None: - runner = CliRunner() - result = runner.invoke(quickstart, ["--help"]) - self.assertEqual(0, result.exit_code) - self.assertIsNone(result.exception) - - def test_local_upgrade_help(self) -> None: - runner = CliRunner() - result = runner.invoke(upgrade, ["--help"]) - self.assertEqual(0, result.exit_code) - self.assertIsNone(result.exception) diff --git a/tutor/commands/compose.py b/tutor/commands/compose.py index ebc265218fd..be3d206f774 100644 --- a/tutor/commands/compose.py +++ b/tutor/commands/compose.py @@ -1,5 +1,5 @@ import os -from typing import List +from typing import List, Optional import click @@ -7,9 +7,12 @@ from .. import config as tutor_config from .. import env as tutor_env from .. import fmt, jobs, utils + from ..exceptions import TutorError from ..types import Config +from .config import save as config_save_command from .context import BaseJobContext +from .upgrade.compose import upgrade_from class ComposeJobRunner(jobs.BaseComposeJobRunner): @@ -60,6 +63,91 @@ def job_runner(self, config: Config) -> ComposeJobRunner: raise NotImplementedError +@click.command(help="Configure and run Open edX from scratch") +@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively") +@click.option("-p", "--pullimages", is_flag=True, help="Update docker images") +@click.pass_context +def quickstart(context: click.Context, non_interactive: bool, pullimages: bool) -> None: + try: + utils.check_macos_docker_memory() + except TutorError as e: + fmt.echo_alert( + f"""Could not verify sufficient RAM allocation in Docker: + + {e} + +Tutor may not work if Docker is configured with < 4 GB RAM. Please follow instructions from: + + https://docs.tutor.overhang.io/install.html""" + ) + + run_upgrade_from_release = tutor_env.should_upgrade_from_release(context.obj.root) + if run_upgrade_from_release is not None: + click.echo(fmt.title("Upgrading from an older release")) + if not non_interactive: + to_release = tutor_env.get_package_release() + question = f"""You are about to upgrade your Open edX platform from {run_upgrade_from_release.capitalize()} to {to_release.capitalize()} + +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=" " + ) + context.invoke( + upgrade, + from_release=run_upgrade_from_release, + ) + + click.echo(fmt.title("Interactive platform configuration")) + context.invoke(config_save_command, interactive=(not non_interactive)) + + if run_upgrade_from_release and not non_interactive: + question = f"""Your platform is being upgraded from {run_upgrade_from_release.capitalize()}. + +If you run custom Docker images, you must rebuild them now by running the following command in a different shell: + + tutor images build all # list your custom images here + +See the documentation for more information: + + https://docs.tutor.overhang.io/install.html#upgrading-to-a-new-open-edx-release + +Press enter when you are ready to continue""" + click.confirm( + fmt.question(question), default=True, abort=True, prompt_suffix=" " + ) + + click.echo(fmt.title("Stopping any existing platform")) + context.invoke(stop) + if pullimages: + click.echo(fmt.title("Docker image updates")) + context.invoke(dc_command, command="pull") + click.echo(fmt.title("Starting the platform in detached mode")) + context.invoke(start, detach=True) + click.echo(fmt.title("Database creation and migrations")) + context.invoke(init) + + config = tutor_config.load(context.obj.root) + fmt.echo_info( + """The Open edX platform is now running in detached mode +Your Open edX platform is ready and can be accessed at the following urls: + + {http}://{lms_host} + {http}://{cms_host} + """.format( + http="https" if config["ENABLE_HTTPS"] else "http", + lms_host=config["LMS_HOST"], + cms_host=config["CMS_HOST"], + ) + ) + + @click.command( short_help="Run all or a selection of services.", help="Run all or a selection of services. Docker images will be rebuilt where necessary.", @@ -263,6 +351,31 @@ def logs(context: click.Context, follow: bool, tail: bool, service: str) -> None context.invoke(dc_command, command="logs", args=args) +@click.command( + short_help="Perform release-specific upgrade tasks", + help="Perform release-specific upgrade tasks. To perform a full upgrade remember to run `quickstart`.", +) +@click.option( + "--from", + "from_release", + type=click.Choice(["ironwood", "juniper", "koa", "lilac"]), +) +@click.pass_context +def upgrade(context: click.Context, from_release: Optional[str]) -> None: + fmt.echo_alert( + "This command only performs a partial upgrade of your Open edX platform. " + "To perform a full upgrade, you should run `tutor local quickstart`." + ) + if from_release is None: + from_release = tutor_env.get_env_release(context.obj.root) + if from_release is None: + fmt.echo_info("Your environment is already up-to-date") + else: + upgrade_from(context, from_release) + # We update the environment to update the version + context.invoke(config_save_command) + + @click.command( short_help="Direct interface to docker-compose.", help=( @@ -294,6 +407,7 @@ def dc_command(context: BaseComposeContext, command: str, args: List[str]) -> No def add_commands(command_group: click.Group) -> None: + command_group.add_command(quickstart) command_group.add_command(start) command_group.add_command(stop) command_group.add_command(restart) diff --git a/tutor/commands/local.py b/tutor/commands/local.py index cd4c6688631..3ec8205e14a 100644 --- a/tutor/commands/local.py +++ b/tutor/commands/local.py @@ -1,13 +1,7 @@ -from typing import Optional - import click -from tutor import config as tutor_config from tutor import env as tutor_env -from tutor import exceptions, fmt, utils from tutor.commands import compose -from tutor.commands.config import save as config_save_command -from tutor.commands.upgrade.local import upgrade_from from tutor.types import Config, get_typed @@ -41,116 +35,4 @@ def local(context: click.Context) -> None: context.obj = LocalContext(context.obj.root) -@click.command(help="Configure and run Open edX from scratch") -@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively") -@click.option("-p", "--pullimages", is_flag=True, help="Update docker images") -@click.pass_context -def quickstart(context: click.Context, non_interactive: bool, pullimages: bool) -> None: - try: - utils.check_macos_docker_memory() - except exceptions.TutorError as e: - fmt.echo_alert( - f"""Could not verify sufficient RAM allocation in Docker: - - {e} - -Tutor may not work if Docker is configured with < 4 GB RAM. Please follow instructions from: - - https://docs.tutor.overhang.io/install.html""" - ) - - run_upgrade_from_release = tutor_env.should_upgrade_from_release(context.obj.root) - if run_upgrade_from_release is not None: - click.echo(fmt.title("Upgrading from an older release")) - if not non_interactive: - to_release = tutor_env.get_package_release() - question = f"""You are about to upgrade your Open edX platform from {run_upgrade_from_release.capitalize()} to {to_release.capitalize()} - -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=" " - ) - context.invoke( - upgrade, - from_release=run_upgrade_from_release, - ) - - click.echo(fmt.title("Interactive platform configuration")) - context.invoke(config_save_command, interactive=(not non_interactive)) - - if run_upgrade_from_release and not non_interactive: - question = f"""Your platform is being upgraded from {run_upgrade_from_release.capitalize()}. - -If you run custom Docker images, you must rebuild them now by running the following command in a different shell: - - tutor images build all # list your custom images here - -See the documentation for more information: - - https://docs.tutor.overhang.io/install.html#upgrading-to-a-new-open-edx-release - -Press enter when you are ready to continue""" - click.confirm( - fmt.question(question), default=True, abort=True, prompt_suffix=" " - ) - - click.echo(fmt.title("Stopping any existing platform")) - context.invoke(compose.stop) - if pullimages: - click.echo(fmt.title("Docker image updates")) - context.invoke(compose.dc_command, command="pull") - click.echo(fmt.title("Starting the platform in detached mode")) - context.invoke(compose.start, detach=True) - click.echo(fmt.title("Database creation and migrations")) - context.invoke(compose.init) - - config = tutor_config.load(context.obj.root) - fmt.echo_info( - """The Open edX platform is now running in detached mode -Your Open edX platform is ready and can be accessed at the following urls: - - {http}://{lms_host} - {http}://{cms_host} - """.format( - http="https" if config["ENABLE_HTTPS"] else "http", - lms_host=config["LMS_HOST"], - cms_host=config["CMS_HOST"], - ) - ) - - -@click.command( - short_help="Perform release-specific upgrade tasks", - help="Perform release-specific upgrade tasks. To perform a full upgrade remember to run `quickstart`.", -) -@click.option( - "--from", - "from_release", - type=click.Choice(["ironwood", "juniper", "koa", "lilac"]), -) -@click.pass_context -def upgrade(context: click.Context, from_release: Optional[str]) -> None: - fmt.echo_alert( - "This command only performs a partial upgrade of your Open edX platform. " - "To perform a full upgrade, you should run `tutor local quickstart`." - ) - if from_release is None: - from_release = tutor_env.get_env_release(context.obj.root) - if from_release is None: - fmt.echo_info("Your environment is already up-to-date") - else: - upgrade_from(context, from_release) - # We update the environment to update the version - context.invoke(config_save_command) - - -local.add_command(quickstart) -local.add_command(upgrade) compose.add_commands(local) diff --git a/tutor/commands/upgrade/local.py b/tutor/commands/upgrade/compose.py similarity index 100% rename from tutor/commands/upgrade/local.py rename to tutor/commands/upgrade/compose.py