mirror of
https://github.com/ChristianLight/tutor.git
synced 2024-12-12 14:17:46 +00:00
Move "-r/--root" option to parent command level
This commit is contained in:
parent
e9f200a102
commit
13de3c8adc
@ -4,6 +4,7 @@ Note: Breaking changes between versions are indicated by "💥".
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- [Improvement] Move ``-r/--root`` option to parent command level
|
||||||
- [Bugfix] Fix course about page visibility
|
- [Bugfix] Fix course about page visibility
|
||||||
- [Improvement] Print gunicorn access logs in the console
|
- [Improvement] Print gunicorn access logs in the console
|
||||||
- 💥[Improvement] Get rid of the `indexcourses` and `portainer` command (#269)
|
- 💥[Improvement] Get rid of the `indexcourses` and `portainer` command (#269)
|
||||||
|
@ -5,7 +5,12 @@ Local deployment
|
|||||||
|
|
||||||
This method is for deploying Open edX locally on a single server, where docker images are orchestrated with `docker-compose <https://docs.docker.com/compose/overview/>`_.
|
This method is for deploying Open edX locally on a single server, where docker images are orchestrated with `docker-compose <https://docs.docker.com/compose/overview/>`_.
|
||||||
|
|
||||||
In the following, environment and data files will be generated in a user-specific project folder which will be referred to as the "**project root**". On Linux, the default project root is ``~/.local/share/tutor``. An alternative project root can be defined by passing the ``--root=...`` option to most commands, or define the ``TUTOR_ROOT=...`` environment variable.
|
In the following, environment and data files will be generated in a user-specific project folder which will be referred to as the "**project root**". On Linux, the default project root is ``~/.local/share/tutor``. An alternative project root can be defined by passing the ``--root=...`` option to the ``tutor`` command, or define the ``TUTOR_ROOT=...`` environment variable::
|
||||||
|
|
||||||
|
tutor --root=/path/to/tutorroot run ...
|
||||||
|
# Or equivalently:
|
||||||
|
export TUTOR_ROOT=/path/to/tutorroot
|
||||||
|
tutor run ...
|
||||||
|
|
||||||
Main commands
|
Main commands
|
||||||
-------------
|
-------------
|
||||||
|
@ -3,7 +3,6 @@ import click
|
|||||||
from .. import config as tutor_config
|
from .. import config as tutor_config
|
||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import opts
|
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
@ -18,31 +17,31 @@ def build():
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Build the application in debug mode")
|
@click.command(help="Build the application in debug mode")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def debug(root):
|
def debug(context):
|
||||||
docker_run(root)
|
docker_run(context.root)
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"The debuggable APK file is available in {}".format(
|
"The debuggable APK file is available in {}".format(
|
||||||
tutor_env.data_path(root, "android")
|
tutor_env.data_path(context.root, "android")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Build the application in release mode")
|
@click.command(help="Build the application in release mode")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def release(root):
|
def release(context):
|
||||||
docker_run(root, "./gradlew", "assembleProdRelease")
|
docker_run(context.root, "./gradlew", "assembleProdRelease")
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"The production APK file is available in {}".format(
|
"The production APK file is available in {}".format(
|
||||||
tutor_env.data_path(root, "android")
|
tutor_env.data_path(context.root, "android")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Pull the docker image")
|
@click.command(help="Pull the docker image")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def pullimage(root):
|
def pullimage(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
utils.execute("docker", "pull", config["DOCKER_IMAGE_ANDROID"])
|
utils.execute("docker", "pull", config["DOCKER_IMAGE_ANDROID"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import appdirs
|
||||||
import click
|
import click
|
||||||
import click_repl
|
import click_repl
|
||||||
|
|
||||||
@ -18,9 +19,15 @@ from .. import exceptions
|
|||||||
from .. import fmt
|
from .. import fmt
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
|
class Context:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
cli()
|
cli() # pylint: disable=no-value-for-parameter
|
||||||
except exceptions.TutorError as e:
|
except exceptions.TutorError as e:
|
||||||
fmt.echo_error("Error: {}".format(e.args[0]))
|
fmt.echo_error("Error: {}".format(e.args[0]))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -28,8 +35,18 @@ def main():
|
|||||||
|
|
||||||
@click.group(context_settings={"help_option_names": ["-h", "--help", "help"]})
|
@click.group(context_settings={"help_option_names": ["-h", "--help", "help"]})
|
||||||
@click.version_option(version=__version__)
|
@click.version_option(version=__version__)
|
||||||
def cli():
|
@click.option(
|
||||||
pass
|
"-r",
|
||||||
|
"--root",
|
||||||
|
envvar="TUTOR_ROOT",
|
||||||
|
default=appdirs.user_data_dir(appname="tutor"),
|
||||||
|
show_default=True,
|
||||||
|
type=click.Path(resolve_path=True),
|
||||||
|
help="Root project directory (environment variable: TUTOR_ROOT)",
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def cli(context, root):
|
||||||
|
context.obj = Context(root)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Print this help", name="help")
|
@click.command(help="Print this help", name="help")
|
||||||
|
@ -5,7 +5,7 @@ from .. import env
|
|||||||
from .. import exceptions
|
from .. import exceptions
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import interactive as interactive_config
|
from .. import interactive as interactive_config
|
||||||
from .. import opts
|
from .. import serialize
|
||||||
|
|
||||||
|
|
||||||
@click.group(
|
@click.group(
|
||||||
@ -17,14 +17,24 @@ def config_command():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class YamlParamType(click.ParamType):
|
||||||
|
name = "yaml"
|
||||||
|
|
||||||
|
def convert(self, value, param, ctx):
|
||||||
|
try:
|
||||||
|
k, v = value.split("=")
|
||||||
|
except ValueError:
|
||||||
|
self.fail("'{}' is not of the form 'key=value'.".format(value), param, ctx)
|
||||||
|
return k, serialize.parse(v)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Create and save configuration interactively")
|
@click.command(help="Create and save configuration interactively")
|
||||||
@opts.root
|
|
||||||
@click.option("-i", "--interactive", is_flag=True, help="Run interactively")
|
@click.option("-i", "--interactive", is_flag=True, help="Run interactively")
|
||||||
@click.option(
|
@click.option(
|
||||||
"-s",
|
"-s",
|
||||||
"--set",
|
"--set",
|
||||||
"set_",
|
"set_",
|
||||||
type=opts.YamlParamType(),
|
type=YamlParamType(),
|
||||||
multiple=True,
|
multiple=True,
|
||||||
metavar="KEY=VAL",
|
metavar="KEY=VAL",
|
||||||
help="Set a configuration value (can be used multiple times)",
|
help="Set a configuration value (can be used multiple times)",
|
||||||
@ -35,28 +45,31 @@ def config_command():
|
|||||||
multiple=True,
|
multiple=True,
|
||||||
help="Remove a configuration value (can be used multiple times)",
|
help="Remove a configuration value (can be used multiple times)",
|
||||||
)
|
)
|
||||||
def save(root, interactive, set_, unset):
|
@click.pass_obj
|
||||||
config, defaults = interactive_config.load_all(root, interactive=interactive)
|
def save(context, interactive, set_, unset):
|
||||||
|
config, defaults = interactive_config.load_all(
|
||||||
|
context.root, interactive=interactive
|
||||||
|
)
|
||||||
if set_:
|
if set_:
|
||||||
tutor_config.merge(config, dict(set_), force=True)
|
tutor_config.merge(config, dict(set_), force=True)
|
||||||
for key in unset:
|
for key in unset:
|
||||||
config.pop(key, None)
|
config.pop(key, None)
|
||||||
tutor_config.save(root, config)
|
tutor_config.save(context.root, config)
|
||||||
tutor_config.merge(config, defaults)
|
tutor_config.merge(config, defaults)
|
||||||
env.save(root, config)
|
env.save(context.root, config)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Print the project root")
|
@click.command(help="Print the project root")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def printroot(root):
|
def printroot(context):
|
||||||
click.echo(root)
|
click.echo(context.root)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Print a configuration value")
|
@click.command(help="Print a configuration value")
|
||||||
@opts.root
|
|
||||||
@click.argument("key")
|
@click.argument("key")
|
||||||
def printvalue(root, key):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def printvalue(context, key):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
try:
|
try:
|
||||||
fmt.echo(config[key])
|
fmt.echo(config[key])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -2,7 +2,6 @@ import click
|
|||||||
|
|
||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import opts
|
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
@ -11,53 +10,72 @@ def dev():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
edx_platform_path_option = click.option(
|
||||||
|
"-P",
|
||||||
|
"--edx-platform-path",
|
||||||
|
envvar="TUTOR_EDX_PLATFORM_PATH",
|
||||||
|
type=click.Path(exists=True, dir_okay=True, resolve_path=True),
|
||||||
|
help="Mount a local edx-platform from the host (environment variable: TUTOR_EDX_PLATFORM_PATH)",
|
||||||
|
)
|
||||||
|
|
||||||
|
edx_platform_development_settings_option = click.option(
|
||||||
|
"-S",
|
||||||
|
"--edx-platform-settings",
|
||||||
|
envvar="TUTOR_EDX_PLATFORM_SETTINGS",
|
||||||
|
default="tutor.development",
|
||||||
|
help="Mount a local edx-platform from the host (environment variable: TUTOR_EDX_PLATFORM_PATH)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
help="Run a command in one of the containers",
|
help="Run a command in one of the containers",
|
||||||
context_settings={"ignore_unknown_options": True},
|
context_settings={"ignore_unknown_options": True},
|
||||||
)
|
)
|
||||||
@opts.root
|
@edx_platform_path_option
|
||||||
@opts.edx_platform_path
|
@edx_platform_development_settings_option
|
||||||
@opts.edx_platform_development_settings
|
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
@click.argument("command", default=None, required=False)
|
@click.argument("command", default=None, required=False)
|
||||||
@click.argument("args", nargs=-1)
|
@click.argument("args", nargs=-1)
|
||||||
def run(root, edx_platform_path, edx_platform_settings, service, command, args):
|
@click.pass_obj
|
||||||
|
def run(context, edx_platform_path, edx_platform_settings, service, command, args):
|
||||||
run_command = [service]
|
run_command = [service]
|
||||||
if command:
|
if command:
|
||||||
run_command.append(command)
|
run_command.append(command)
|
||||||
if args:
|
if args:
|
||||||
run_command += args
|
run_command += args
|
||||||
docker_compose_run(root, edx_platform_path, edx_platform_settings, *run_command)
|
docker_compose_run(
|
||||||
|
context.root, edx_platform_path, edx_platform_settings, *run_command
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
help="Exec a command in a running container",
|
help="Exec a command in a running container",
|
||||||
context_settings={"ignore_unknown_options": True},
|
context_settings={"ignore_unknown_options": True},
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
@click.argument("command")
|
@click.argument("command")
|
||||||
@click.argument("args", nargs=-1)
|
@click.argument("args", nargs=-1)
|
||||||
def execute(root, service, command, args):
|
@click.pass_obj
|
||||||
|
def execute(context, service, command, args):
|
||||||
exec_command = ["exec", service, command]
|
exec_command = ["exec", service, command]
|
||||||
if args:
|
if args:
|
||||||
exec_command += args
|
exec_command += args
|
||||||
docker_compose(root, *exec_command)
|
docker_compose(context.root, *exec_command)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Run a development server")
|
@click.command(help="Run a development server")
|
||||||
@opts.root
|
@edx_platform_path_option
|
||||||
@opts.edx_platform_path
|
@edx_platform_development_settings_option
|
||||||
@opts.edx_platform_development_settings
|
|
||||||
@click.argument("service", type=click.Choice(["lms", "cms"]))
|
@click.argument("service", type=click.Choice(["lms", "cms"]))
|
||||||
def runserver(root, edx_platform_path, edx_platform_settings, service):
|
@click.pass_obj
|
||||||
|
def runserver(context, edx_platform_path, edx_platform_settings, service):
|
||||||
port = service_port(service)
|
port = service_port(service)
|
||||||
|
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"The {} service will be available at http://localhost:{}".format(service, port)
|
"The {} service will be available at http://localhost:{}".format(service, port)
|
||||||
)
|
)
|
||||||
docker_compose_run(
|
docker_compose_run(
|
||||||
root,
|
context.root,
|
||||||
edx_platform_path,
|
edx_platform_path,
|
||||||
edx_platform_settings,
|
edx_platform_settings,
|
||||||
"-p",
|
"-p",
|
||||||
@ -71,9 +89,9 @@ def runserver(root, edx_platform_path, edx_platform_settings, service):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Stop a running development platform")
|
@click.command(help="Stop a running development platform")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def stop(root):
|
def stop(context):
|
||||||
docker_compose(root, "rm", "--stop", "--force")
|
docker_compose(context.root, "rm", "--stop", "--force")
|
||||||
|
|
||||||
|
|
||||||
def docker_compose_run(root, edx_platform_path, edx_platform_settings, *command):
|
def docker_compose_run(root, edx_platform_path, edx_platform_settings, *command):
|
||||||
|
@ -5,7 +5,6 @@ import click
|
|||||||
from .. import config as tutor_config
|
from .. import config as tutor_config
|
||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import images
|
from .. import images
|
||||||
from .. import opts
|
|
||||||
from .. import plugins
|
from .. import plugins
|
||||||
|
|
||||||
BASE_IMAGE_NAMES = ["openedx", "forum", "android"]
|
BASE_IMAGE_NAMES = ["openedx", "forum", "android"]
|
||||||
@ -21,7 +20,6 @@ def images_command():
|
|||||||
short_help="Build docker images",
|
short_help="Build docker images",
|
||||||
help="Build the docker images necessary for an Open edX platform.",
|
help="Build the docker images necessary for an Open edX platform.",
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.argument("image", nargs=-1)
|
@click.argument("image", nargs=-1)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--no-cache", is_flag=True, help="Do not use cache when building the image"
|
"--no-cache", is_flag=True, help="Do not use cache when building the image"
|
||||||
@ -32,10 +30,11 @@ def images_command():
|
|||||||
multiple=True,
|
multiple=True,
|
||||||
help="Set build-time docker ARGS in the form 'myarg=value'. This option may be specified multiple times.",
|
help="Set build-time docker ARGS in the form 'myarg=value'. This option may be specified multiple times.",
|
||||||
)
|
)
|
||||||
def build(root, image, no_cache, build_arg):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def build(context, image, no_cache, build_arg):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
for i in image:
|
for i in image:
|
||||||
build_image(root, config, i, no_cache, build_arg)
|
build_image(context.root, config, i, no_cache, build_arg)
|
||||||
|
|
||||||
|
|
||||||
def build_image(root, config, image, no_cache, build_arg):
|
def build_image(root, config, image, no_cache, build_arg):
|
||||||
@ -77,10 +76,10 @@ def build_image(root, config, image, no_cache, build_arg):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(short_help="Pull images from the Docker registry")
|
@click.command(short_help="Pull images from the Docker registry")
|
||||||
@opts.root
|
|
||||||
@click.argument("image", nargs=-1)
|
@click.argument("image", nargs=-1)
|
||||||
def pull(root, image):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def pull(context, image):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
for i in image:
|
for i in image:
|
||||||
pull_image(config, i)
|
pull_image(config, i)
|
||||||
|
|
||||||
@ -101,15 +100,15 @@ def pull_image(config, image):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(short_help="Push images to the Docker registry")
|
@click.command(short_help="Push images to the Docker registry")
|
||||||
@opts.root
|
|
||||||
@click.argument("image", nargs=-1)
|
@click.argument("image", nargs=-1)
|
||||||
def push(root, image):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def push(context, image):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
for i in image:
|
for i in image:
|
||||||
push_image(root, config, i)
|
push_image(config, i)
|
||||||
|
|
||||||
|
|
||||||
def push_image(root, config, image):
|
def push_image(config, image):
|
||||||
# Push base images
|
# Push base images
|
||||||
for img in BASE_IMAGE_NAMES:
|
for img in BASE_IMAGE_NAMES:
|
||||||
if image in [img, "all"]:
|
if image in [img, "all"]:
|
||||||
|
@ -4,7 +4,6 @@ from .. import config as tutor_config
|
|||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import interactive as interactive_config
|
from .. import interactive as interactive_config
|
||||||
from .. import opts
|
|
||||||
from .. import scripts
|
from .. import scripts
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
@ -15,11 +14,11 @@ def k8s():
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Configure and run Open edX from scratch")
|
@click.command(help="Configure and run Open edX from scratch")
|
||||||
@opts.root
|
|
||||||
@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively")
|
@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively")
|
||||||
def quickstart(root, non_interactive):
|
@click.pass_obj
|
||||||
|
def quickstart(context, non_interactive):
|
||||||
click.echo(fmt.title("Interactive platform configuration"))
|
click.echo(fmt.title("Interactive platform configuration"))
|
||||||
config = interactive_config.update(root, interactive=(not non_interactive))
|
config = interactive_config.update(context.root, interactive=(not non_interactive))
|
||||||
if config["ACTIVATE_HTTPS"] and not config["WEB_PROXY"]:
|
if config["ACTIVATE_HTTPS"] and not config["WEB_PROXY"]:
|
||||||
fmt.echo_alert(
|
fmt.echo_alert(
|
||||||
"Potentially invalid configuration: ACTIVATE_HTTPS=true WEB_PROXY=false\n"
|
"Potentially invalid configuration: ACTIVATE_HTTPS=true WEB_PROXY=false\n"
|
||||||
@ -28,21 +27,21 @@ def quickstart(root, non_interactive):
|
|||||||
" more information."
|
" more information."
|
||||||
)
|
)
|
||||||
click.echo(fmt.title("Updating the current environment"))
|
click.echo(fmt.title("Updating the current environment"))
|
||||||
tutor_env.save(root, config)
|
tutor_env.save(context, config)
|
||||||
click.echo(fmt.title("Starting the platform"))
|
click.echo(fmt.title("Starting the platform"))
|
||||||
start.callback(root)
|
start.callback()
|
||||||
click.echo(fmt.title("Database creation and migrations"))
|
click.echo(fmt.title("Database creation and migrations"))
|
||||||
init.callback(root)
|
init.callback()
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Run all configured Open edX services")
|
@click.command(help="Run all configured Open edX services")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def start(root):
|
def start(context):
|
||||||
# Create namespace
|
# Create namespace
|
||||||
utils.kubectl(
|
utils.kubectl(
|
||||||
"apply",
|
"apply",
|
||||||
"--kustomize",
|
"--kustomize",
|
||||||
tutor_env.pathjoin(root),
|
tutor_env.pathjoin(context.root),
|
||||||
"--wait",
|
"--wait",
|
||||||
"--selector",
|
"--selector",
|
||||||
"app.kubernetes.io/component=namespace",
|
"app.kubernetes.io/component=namespace",
|
||||||
@ -51,29 +50,29 @@ def start(root):
|
|||||||
utils.kubectl(
|
utils.kubectl(
|
||||||
"apply",
|
"apply",
|
||||||
"--kustomize",
|
"--kustomize",
|
||||||
tutor_env.pathjoin(root),
|
tutor_env.pathjoin(context.root),
|
||||||
"--wait",
|
"--wait",
|
||||||
"--selector",
|
"--selector",
|
||||||
"app.kubernetes.io/component=volume",
|
"app.kubernetes.io/component=volume",
|
||||||
)
|
)
|
||||||
# Create everything else
|
# Create everything else
|
||||||
utils.kubectl("apply", "--kustomize", tutor_env.pathjoin(root))
|
utils.kubectl("apply", "--kustomize", tutor_env.pathjoin(context.root))
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Stop a running platform")
|
@click.command(help="Stop a running platform")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def stop(root):
|
def stop(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
utils.kubectl(
|
utils.kubectl(
|
||||||
"delete", *resource_selector(config), "deployments,services,ingress,configmaps"
|
"delete", *resource_selector(config), "deployments,services,ingress,configmaps"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Reboot an existing platform")
|
@click.command(help="Reboot an existing platform")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def reboot(root):
|
def reboot(context):
|
||||||
stop.callback(root)
|
stop.callback()
|
||||||
start.callback(root)
|
start.callback()
|
||||||
|
|
||||||
|
|
||||||
def resource_selector(config, *selectors):
|
def resource_selector(config, *selectors):
|
||||||
@ -87,24 +86,28 @@ def resource_selector(config, *selectors):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Completely delete an existing platform")
|
@click.command(help="Completely delete an existing platform")
|
||||||
@opts.root
|
|
||||||
@click.option("-y", "--yes", is_flag=True, help="Do not ask for confirmation")
|
@click.option("-y", "--yes", is_flag=True, help="Do not ask for confirmation")
|
||||||
def delete(root, yes):
|
@click.pass_obj
|
||||||
|
def delete(context, yes):
|
||||||
if not yes:
|
if not yes:
|
||||||
click.confirm(
|
click.confirm(
|
||||||
"Are you sure you want to delete the platform? All data will be removed.",
|
"Are you sure you want to delete the platform? All data will be removed.",
|
||||||
abort=True,
|
abort=True,
|
||||||
)
|
)
|
||||||
utils.kubectl(
|
utils.kubectl(
|
||||||
"delete", "-k", tutor_env.pathjoin(root), "--ignore-not-found=true", "--wait"
|
"delete",
|
||||||
|
"-k",
|
||||||
|
tutor_env.pathjoin(context.root),
|
||||||
|
"--ignore-not-found=true",
|
||||||
|
"--wait",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Initialise all applications")
|
@click.command(help="Initialise all applications")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def init(root):
|
def init(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
runner = K8sScriptRunner(root, config)
|
runner = K8sScriptRunner(context.root, config)
|
||||||
for service in ["mysql", "elasticsearch", "mongodb"]:
|
for service in ["mysql", "elasticsearch", "mongodb"]:
|
||||||
if runner.is_activated(service):
|
if runner.is_activated(service):
|
||||||
wait_for_pod_ready(config, service)
|
wait_for_pod_ready(config, service)
|
||||||
@ -112,7 +115,6 @@ def init(root):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Create an Open edX user and interactively set their password")
|
@click.command(help="Create an Open edX user and interactively set their password")
|
||||||
@opts.root
|
|
||||||
@click.option("--superuser", is_flag=True, help="Make superuser")
|
@click.option("--superuser", is_flag=True, help="Make superuser")
|
||||||
@click.option("--staff", is_flag=True, help="Make staff user")
|
@click.option("--staff", is_flag=True, help="Make staff user")
|
||||||
@click.option(
|
@click.option(
|
||||||
@ -122,9 +124,10 @@ def init(root):
|
|||||||
)
|
)
|
||||||
@click.argument("name")
|
@click.argument("name")
|
||||||
@click.argument("email")
|
@click.argument("email")
|
||||||
def createuser(root, superuser, staff, password, name, email):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def createuser(context, superuser, staff, password, name, email):
|
||||||
runner = K8sScriptRunner(root, config)
|
config = tutor_config.load(context.root)
|
||||||
|
runner = K8sScriptRunner(context.root, config)
|
||||||
runner.check_service_is_activated("lms")
|
runner.check_service_is_activated("lms")
|
||||||
command = scripts.create_user_command(
|
command = scripts.create_user_command(
|
||||||
superuser, staff, name, email, password=password
|
superuser, staff, name, email, password=password
|
||||||
@ -133,31 +136,31 @@ def createuser(root, superuser, staff, password, name, email):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Import the demo course")
|
@click.command(help="Import the demo course")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def importdemocourse(root):
|
def importdemocourse(context):
|
||||||
fmt.echo_info("Importing demo course")
|
fmt.echo_info("Importing demo course")
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
runner = K8sScriptRunner(root, config)
|
runner = K8sScriptRunner(context.root, config)
|
||||||
scripts.import_demo_course(runner)
|
scripts.import_demo_course(runner)
|
||||||
|
|
||||||
|
|
||||||
@click.command(name="exec", help="Execute a command in a pod of the given application")
|
@click.command(name="exec", help="Execute a command in a pod of the given application")
|
||||||
@opts.root
|
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
@click.argument("command")
|
@click.argument("command")
|
||||||
def exec_command(root, service, command):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def exec_command(context, service, command):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
kubectl_exec(config, service, command, attach=True)
|
kubectl_exec(config, service, command, attach=True)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="View output from containers")
|
@click.command(help="View output from containers")
|
||||||
@opts.root
|
|
||||||
@click.option("-c", "--container", help="Print the logs of this specific container")
|
@click.option("-c", "--container", help="Print the logs of this specific container")
|
||||||
@click.option("-f", "--follow", is_flag=True, help="Follow log output")
|
@click.option("-f", "--follow", is_flag=True, help="Follow log output")
|
||||||
@click.option("--tail", type=int, help="Number of lines to show from each container")
|
@click.option("--tail", type=int, help="Number of lines to show from each container")
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
def logs(root, container, follow, tail, service):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def logs(context, container, follow, tail, service):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
|
|
||||||
command = ["logs"]
|
command = ["logs"]
|
||||||
selectors = ["app.kubernetes.io/name=" + service] if service else []
|
selectors = ["app.kubernetes.io/name=" + service] if service else []
|
||||||
|
@ -6,7 +6,6 @@ from .. import config as tutor_config
|
|||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import interactive as interactive_config
|
from .. import interactive as interactive_config
|
||||||
from .. import opts
|
|
||||||
from .. import scripts
|
from .. import scripts
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
@ -20,48 +19,48 @@ def local():
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Configure and run Open edX from scratch")
|
@click.command(help="Configure and run Open edX from scratch")
|
||||||
@opts.root
|
|
||||||
@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.option(
|
@click.option(
|
||||||
"-p", "--pullimages", "pullimages_", is_flag=True, help="Update docker images"
|
"-p", "--pullimages", "pullimages_", is_flag=True, help="Update docker images"
|
||||||
)
|
)
|
||||||
def quickstart(root, non_interactive, pullimages_):
|
@click.pass_obj
|
||||||
|
def quickstart(context, non_interactive, pullimages_):
|
||||||
click.echo(fmt.title("Interactive platform configuration"))
|
click.echo(fmt.title("Interactive platform configuration"))
|
||||||
config = interactive_config.update(root, interactive=(not non_interactive))
|
config = interactive_config.update(context.root, interactive=(not non_interactive))
|
||||||
click.echo(fmt.title("Updating the current environment"))
|
click.echo(fmt.title("Updating the current environment"))
|
||||||
tutor_env.save(root, config)
|
tutor_env.save(context.root, config)
|
||||||
click.echo(fmt.title("Stopping any existing platform"))
|
click.echo(fmt.title("Stopping any existing platform"))
|
||||||
stop.callback(root, [])
|
stop.callback([])
|
||||||
click.echo(fmt.title("HTTPS certificates generation"))
|
click.echo(fmt.title("HTTPS certificates generation"))
|
||||||
https_create.callback(root)
|
https_create.callback()
|
||||||
if pullimages_:
|
if pullimages_:
|
||||||
click.echo(fmt.title("Docker image updates"))
|
click.echo(fmt.title("Docker image updates"))
|
||||||
pullimages.callback(root)
|
pullimages.callback()
|
||||||
click.echo(fmt.title("Starting the platform in detached mode"))
|
click.echo(fmt.title("Starting the platform in detached mode"))
|
||||||
start.callback(root, True, [])
|
start.callback(True, [])
|
||||||
click.echo(fmt.title("Database creation and migrations"))
|
click.echo(fmt.title("Database creation and migrations"))
|
||||||
init.callback(root)
|
init.callback()
|
||||||
echo_platform_info(config)
|
echo_platform_info(config)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Update docker images")
|
@click.command(help="Update docker images")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def pullimages(root):
|
def pullimages(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
docker_compose(root, config, "pull")
|
docker_compose(context.root, config, "pull")
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Run all or a selection of configured Open edX services")
|
@click.command(help="Run all or a selection of configured Open edX services")
|
||||||
@opts.root
|
|
||||||
@click.option("-d", "--detach", is_flag=True, help="Start in daemon mode")
|
@click.option("-d", "--detach", is_flag=True, help="Start in daemon mode")
|
||||||
@click.argument("services", metavar="service", nargs=-1)
|
@click.argument("services", metavar="service", nargs=-1)
|
||||||
def start(root, detach, services):
|
@click.pass_obj
|
||||||
|
def start(context, detach, services):
|
||||||
command = ["up", "--remove-orphans"]
|
command = ["up", "--remove-orphans"]
|
||||||
if detach:
|
if detach:
|
||||||
command.append("-d")
|
command.append("-d")
|
||||||
|
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
docker_compose(root, config, *command, *services)
|
docker_compose(context.root, config, *command, *services)
|
||||||
|
|
||||||
|
|
||||||
def echo_platform_info(config):
|
def echo_platform_info(config):
|
||||||
@ -82,23 +81,22 @@ def echo_platform_info(config):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Stop a running platform")
|
@click.command(help="Stop a running platform")
|
||||||
@opts.root
|
|
||||||
@click.argument("services", metavar="service", nargs=-1)
|
@click.argument("services", metavar="service", nargs=-1)
|
||||||
def stop(root, services):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def stop(context, services):
|
||||||
docker_compose(root, config, "rm", "--stop", "--force", *services)
|
config = tutor_config.load(context.root)
|
||||||
|
docker_compose(context.root, config, "rm", "--stop", "--force", *services)
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
short_help="Reboot an existing platform",
|
short_help="Reboot an existing platform",
|
||||||
help="This is more than just a restart: with reboot, the platform is fully stopped before being restarted again",
|
help="This is more than just a restart: with reboot, the platform is fully stopped before being restarted again",
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.option("-d", "--detach", is_flag=True, help="Start in daemon mode")
|
@click.option("-d", "--detach", is_flag=True, help="Start in daemon mode")
|
||||||
@click.argument("services", metavar="service", nargs=-1)
|
@click.argument("services", metavar="service", nargs=-1)
|
||||||
def reboot(root, detach, services):
|
def reboot(detach, services):
|
||||||
stop.callback(root, services)
|
stop.callback(services)
|
||||||
start.callback(root, detach, services)
|
start.callback(detach, services)
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
@ -108,10 +106,10 @@ restart all services. Note that this performs a 'docker-compose restart', so new
|
|||||||
may not be taken into account. It is useful for reloading settings, for instance. To
|
may not be taken into account. It is useful for reloading settings, for instance. To
|
||||||
fully stop the platform, use the 'reboot' command.""",
|
fully stop the platform, use the 'reboot' command.""",
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
def restart(root, service):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def restart(context, service):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
command = ["restart"]
|
command = ["restart"]
|
||||||
if service == "openedx":
|
if service == "openedx":
|
||||||
if config["ACTIVATE_LMS"]:
|
if config["ACTIVATE_LMS"]:
|
||||||
@ -120,19 +118,19 @@ def restart(root, service):
|
|||||||
command += ["cms", "cms_worker"]
|
command += ["cms", "cms_worker"]
|
||||||
elif service != "all":
|
elif service != "all":
|
||||||
command += [service]
|
command += [service]
|
||||||
docker_compose(root, config, *command)
|
docker_compose(context.root, config, *command)
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
help="Run a command in one of the containers",
|
help="Run a command in one of the containers",
|
||||||
context_settings={"ignore_unknown_options": True},
|
context_settings={"ignore_unknown_options": True},
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.option("--entrypoint", help="Override the entrypoint of the image")
|
@click.option("--entrypoint", help="Override the entrypoint of the image")
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
@click.argument("command", default=None, required=False)
|
@click.argument("command", default=None, required=False)
|
||||||
@click.argument("args", nargs=-1)
|
@click.argument("args", nargs=-1)
|
||||||
def run(root, entrypoint, service, command, args):
|
@click.pass_obj
|
||||||
|
def run(context, entrypoint, service, command, args):
|
||||||
run_command = ["run", "--rm"]
|
run_command = ["run", "--rm"]
|
||||||
if entrypoint:
|
if entrypoint:
|
||||||
run_command += ["--entrypoint", entrypoint]
|
run_command += ["--entrypoint", entrypoint]
|
||||||
@ -141,31 +139,31 @@ def run(root, entrypoint, service, command, args):
|
|||||||
run_command.append(command)
|
run_command.append(command)
|
||||||
if args:
|
if args:
|
||||||
run_command += args
|
run_command += args
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
docker_compose(root, config, *run_command)
|
docker_compose(context.root, config, *run_command)
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
help="Exec a command in a running container",
|
help="Exec a command in a running container",
|
||||||
context_settings={"ignore_unknown_options": True},
|
context_settings={"ignore_unknown_options": True},
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
@click.argument("command")
|
@click.argument("command")
|
||||||
@click.argument("args", nargs=-1)
|
@click.argument("args", nargs=-1)
|
||||||
def execute(root, service, command, args):
|
@click.pass_obj
|
||||||
|
def execute(context, service, command, args):
|
||||||
exec_command = ["exec", service, command]
|
exec_command = ["exec", service, command]
|
||||||
if args:
|
if args:
|
||||||
exec_command += args
|
exec_command += args
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
docker_compose(root, config, *exec_command)
|
docker_compose(context.root, config, *exec_command)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Initialise all applications")
|
@click.command(help="Initialise all applications")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def init(root):
|
def init(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
runner = ScriptRunner(root, config)
|
runner = ScriptRunner(context.root, config)
|
||||||
scripts.initialise(runner)
|
scripts.initialise(runner)
|
||||||
|
|
||||||
|
|
||||||
@ -179,12 +177,12 @@ that is convenient when developing new plugins. Ex:
|
|||||||
tutor local hook discovery discovery hooks discovery init""",
|
tutor local hook discovery discovery hooks discovery init""",
|
||||||
name="hook",
|
name="hook",
|
||||||
)
|
)
|
||||||
@opts.root
|
|
||||||
@click.argument("service")
|
@click.argument("service")
|
||||||
@click.argument("path", nargs=-1)
|
@click.argument("path", nargs=-1)
|
||||||
def run_hook(root, service, path):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def run_hook(context, service, path):
|
||||||
runner = ScriptRunner(root, config)
|
config = tutor_config.load(context.root)
|
||||||
|
runner = ScriptRunner(context.root, config)
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"Running '{}' hook in '{}' container...".format(".".join(path), service)
|
"Running '{}' hook in '{}' container...".format(".".join(path), service)
|
||||||
)
|
)
|
||||||
@ -197,8 +195,8 @@ def https():
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Create https certificates", name="create")
|
@click.command(help="Create https certificates", name="create")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def https_create(root):
|
def https_create(context):
|
||||||
"""
|
"""
|
||||||
Note: there are a couple issues with https certificate generation.
|
Note: there are a couple issues with https certificate generation.
|
||||||
1. Certificates are generated and renewed by using port 80, which is not necessarily open.
|
1. Certificates are generated and renewed by using port 80, which is not necessarily open.
|
||||||
@ -206,8 +204,8 @@ def https_create(root):
|
|||||||
b. It may be occupied by an external web server
|
b. It may be occupied by an external web server
|
||||||
2. On certificate renewal, nginx is not reloaded
|
2. On certificate renewal, nginx is not reloaded
|
||||||
"""
|
"""
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
runner = ScriptRunner(root, config)
|
runner = ScriptRunner(context.root, config)
|
||||||
if not config["ACTIVATE_HTTPS"]:
|
if not config["ACTIVATE_HTTPS"]:
|
||||||
fmt.echo_info("HTTPS is not activated: certificate generation skipped")
|
fmt.echo_info("HTTPS is not activated: certificate generation skipped")
|
||||||
return
|
return
|
||||||
@ -230,7 +228,7 @@ See the official certbot documentation for your platform: https://certbot.eff.or
|
|||||||
|
|
||||||
utils.docker_run(
|
utils.docker_run(
|
||||||
"--volume",
|
"--volume",
|
||||||
"{}:/etc/letsencrypt/".format(tutor_env.data_path(root, "letsencrypt")),
|
"{}:/etc/letsencrypt/".format(tutor_env.data_path(context.root, "letsencrypt")),
|
||||||
"-p",
|
"-p",
|
||||||
"80:80",
|
"80:80",
|
||||||
"--entrypoint=sh",
|
"--entrypoint=sh",
|
||||||
@ -242,9 +240,9 @@ See the official certbot documentation for your platform: https://certbot.eff.or
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Renew https certificates", name="renew")
|
@click.command(help="Renew https certificates", name="renew")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def https_renew(root):
|
def https_renew(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
if not config["ACTIVATE_HTTPS"]:
|
if not config["ACTIVATE_HTTPS"]:
|
||||||
fmt.echo_info("HTTPS is not activated: certificate renewal skipped")
|
fmt.echo_info("HTTPS is not activated: certificate renewal skipped")
|
||||||
return
|
return
|
||||||
@ -261,7 +259,7 @@ See the official certbot documentation for your platform: https://certbot.eff.or
|
|||||||
return
|
return
|
||||||
docker_run = [
|
docker_run = [
|
||||||
"--volume",
|
"--volume",
|
||||||
"{}:/etc/letsencrypt/".format(tutor_env.data_path(root, "letsencrypt")),
|
"{}:/etc/letsencrypt/".format(tutor_env.data_path(context.root, "letsencrypt")),
|
||||||
"-p",
|
"-p",
|
||||||
"80:80",
|
"80:80",
|
||||||
"certbot/certbot:latest",
|
"certbot/certbot:latest",
|
||||||
@ -271,23 +269,22 @@ See the official certbot documentation for your platform: https://certbot.eff.or
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="View output from containers")
|
@click.command(help="View output from containers")
|
||||||
@opts.root
|
|
||||||
@click.option("-f", "--follow", is_flag=True, help="Follow log output")
|
@click.option("-f", "--follow", is_flag=True, help="Follow log output")
|
||||||
@click.option("--tail", type=int, help="Number of lines to show from each container")
|
@click.option("--tail", type=int, help="Number of lines to show from each container")
|
||||||
@click.argument("service", nargs=-1)
|
@click.argument("service", nargs=-1)
|
||||||
def logs(root, follow, tail, service):
|
@click.pass_obj
|
||||||
|
def logs(context, follow, tail, service):
|
||||||
command = ["logs"]
|
command = ["logs"]
|
||||||
if follow:
|
if follow:
|
||||||
command += ["--follow"]
|
command += ["--follow"]
|
||||||
if tail is not None:
|
if tail is not None:
|
||||||
command += ["--tail", str(tail)]
|
command += ["--tail", str(tail)]
|
||||||
command += service
|
command += service
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
docker_compose(root, config, *command)
|
docker_compose(context.root, config, *command)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Create an Open edX user and interactively set their password")
|
@click.command(help="Create an Open edX user and interactively set their password")
|
||||||
@opts.root
|
|
||||||
@click.option("--superuser", is_flag=True, help="Make superuser")
|
@click.option("--superuser", is_flag=True, help="Make superuser")
|
||||||
@click.option("--staff", is_flag=True, help="Make staff user")
|
@click.option("--staff", is_flag=True, help="Make staff user")
|
||||||
@click.option(
|
@click.option(
|
||||||
@ -297,9 +294,10 @@ def logs(root, follow, tail, service):
|
|||||||
)
|
)
|
||||||
@click.argument("name")
|
@click.argument("name")
|
||||||
@click.argument("email")
|
@click.argument("email")
|
||||||
def createuser(root, superuser, staff, password, name, email):
|
@click.pass_obj
|
||||||
config = tutor_config.load(root)
|
def createuser(context, superuser, staff, password, name, email):
|
||||||
runner = ScriptRunner(root, config)
|
config = tutor_config.load(context.root)
|
||||||
|
runner = ScriptRunner(context.root, config)
|
||||||
runner.check_service_is_activated("lms")
|
runner.check_service_is_activated("lms")
|
||||||
command = scripts.create_user_command(
|
command = scripts.create_user_command(
|
||||||
superuser, staff, name, email, password=password
|
superuser, staff, name, email, password=password
|
||||||
@ -308,10 +306,10 @@ def createuser(root, superuser, staff, password, name, email):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Import the demo course")
|
@click.command(help="Import the demo course")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def importdemocourse(root):
|
def importdemocourse(context):
|
||||||
config = tutor_config.load(root)
|
config = tutor_config.load(context.root)
|
||||||
runner = ScriptRunner(root, config)
|
runner = ScriptRunner(context.root, config)
|
||||||
fmt.echo_info("Importing demo course")
|
fmt.echo_info("Importing demo course")
|
||||||
scripts.import_demo_course(runner)
|
scripts.import_demo_course(runner)
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import click
|
|||||||
from .. import config as tutor_config
|
from .. import config as tutor_config
|
||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import opts
|
|
||||||
from .. import plugins
|
from .. import plugins
|
||||||
|
|
||||||
|
|
||||||
@ -22,47 +21,57 @@ def plugins_command():
|
|||||||
|
|
||||||
|
|
||||||
@click.command(name="list", help="List installed plugins")
|
@click.command(name="list", help="List installed plugins")
|
||||||
@opts.root
|
@click.pass_obj
|
||||||
def list_command(root):
|
def list_command(context):
|
||||||
config = tutor_config.load_user(root)
|
config = tutor_config.load_user(context.root)
|
||||||
for name, _ in plugins.iter_installed():
|
for name, _ in plugins.iter_installed():
|
||||||
status = "" if plugins.is_enabled(config, name) else " (disabled)"
|
status = "" if plugins.is_enabled(config, name) else " (disabled)"
|
||||||
print("{plugin}{status}".format(plugin=name, status=status))
|
print("{plugin}{status}".format(plugin=name, status=status))
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Enable a plugin")
|
@click.command(help="Enable a plugin")
|
||||||
@opts.root
|
|
||||||
@click.argument("plugin_names", metavar="plugin", nargs=-1)
|
@click.argument("plugin_names", metavar="plugin", nargs=-1)
|
||||||
def enable(root, plugin_names):
|
@click.pass_obj
|
||||||
config = tutor_config.load_user(root)
|
def enable(context, plugin_names):
|
||||||
|
config = tutor_config.load_user(context.root)
|
||||||
for plugin in plugin_names:
|
for plugin in plugin_names:
|
||||||
plugins.enable(config, plugin)
|
plugins.enable(config, plugin)
|
||||||
fmt.echo_info("Plugin {} enabled".format(plugin))
|
fmt.echo_info("Plugin {} enabled".format(plugin))
|
||||||
tutor_config.save(root, config)
|
tutor_config.save(context.root, config)
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"You should now re-generate your environment with `tutor config save`."
|
"You should now re-generate your environment with `tutor config save`."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Disable a plugin")
|
@click.command(help="Disable a plugin")
|
||||||
@opts.root
|
|
||||||
@click.argument("plugin_names", metavar="plugin", nargs=-1)
|
@click.argument("plugin_names", metavar="plugin", nargs=-1)
|
||||||
def disable(root, plugin_names):
|
@click.pass_obj
|
||||||
config = tutor_config.load_user(root)
|
def disable(context, plugin_names):
|
||||||
|
config = tutor_config.load_user(context.root)
|
||||||
for plugin in plugin_names:
|
for plugin in plugin_names:
|
||||||
plugins.disable(config, plugin)
|
plugins.disable(config, plugin)
|
||||||
|
|
||||||
plugin_dir = tutor_env.pathjoin(root, "plugins", plugin)
|
plugin_dir = tutor_env.pathjoin(context.root, "plugins", plugin)
|
||||||
if os.path.exists(plugin_dir):
|
if os.path.exists(plugin_dir):
|
||||||
shutil.rmtree(plugin_dir)
|
shutil.rmtree(plugin_dir)
|
||||||
fmt.echo_info("Plugin {} disabled".format(plugin))
|
fmt.echo_info("Plugin {} disabled".format(plugin))
|
||||||
|
|
||||||
tutor_config.save(root, config)
|
tutor_config.save(context.root, config)
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"You should now re-generate your environment with `tutor config save`."
|
"You should now re-generate your environment with `tutor config save`."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_extra_plugin_commands():
|
||||||
|
"""
|
||||||
|
TODO document this. Merge with plugins.iter_commands? It's good to keepo
|
||||||
|
click-related stuff outside of the plugins module.
|
||||||
|
"""
|
||||||
|
for plugin_name, command in plugins.iter_commands():
|
||||||
|
command.name = plugin_name
|
||||||
|
yield command
|
||||||
|
|
||||||
|
|
||||||
plugins_command.add_command(list_command)
|
plugins_command.add_command(list_command)
|
||||||
plugins_command.add_command(enable)
|
plugins_command.add_command(enable)
|
||||||
plugins_command.add_command(disable)
|
plugins_command.add_command(disable)
|
||||||
|
@ -11,7 +11,6 @@ import click
|
|||||||
# Note: it is important that this module does not depend on config, such that
|
# Note: it is important that this module does not depend on config, such that
|
||||||
# the web ui can be launched even where there is no configuration.
|
# the web ui can be launched even where there is no configuration.
|
||||||
from .. import fmt
|
from .. import fmt
|
||||||
from .. import opts
|
|
||||||
from .. import env as tutor_env
|
from .. import env as tutor_env
|
||||||
from .. import serialize
|
from .. import serialize
|
||||||
|
|
||||||
@ -24,7 +23,6 @@ def webui():
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Start the web UI")
|
@click.command(help="Start the web UI")
|
||||||
@opts.root
|
|
||||||
@click.option(
|
@click.option(
|
||||||
"-p",
|
"-p",
|
||||||
"--port",
|
"--port",
|
||||||
@ -36,15 +34,16 @@ def webui():
|
|||||||
@click.option(
|
@click.option(
|
||||||
"-h", "--host", default="0.0.0.0", show_default=True, help="Host address to listen"
|
"-h", "--host", default="0.0.0.0", show_default=True, help="Host address to listen"
|
||||||
)
|
)
|
||||||
def start(root, port, host):
|
@click.pass_obj
|
||||||
check_gotty_binary(root)
|
def start(context, port, host):
|
||||||
|
check_gotty_binary(context.root)
|
||||||
fmt.echo_info("Access the Tutor web UI at http://{}:{}".format(host, port))
|
fmt.echo_info("Access the Tutor web UI at http://{}:{}".format(host, port))
|
||||||
while True:
|
while True:
|
||||||
config = load_config(root)
|
config = load_config(context.root)
|
||||||
user = config["user"]
|
user = config["user"]
|
||||||
password = config["password"]
|
password = config["password"]
|
||||||
command = [
|
command = [
|
||||||
gotty_path(root),
|
gotty_path(context.root),
|
||||||
"--permit-write",
|
"--permit-write",
|
||||||
"--address",
|
"--address",
|
||||||
host,
|
host,
|
||||||
@ -66,7 +65,7 @@ def start(root, port, host):
|
|||||||
try:
|
try:
|
||||||
p.wait(timeout=2)
|
p.wait(timeout=2)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
new_config = load_config(root)
|
new_config = load_config(context.root)
|
||||||
if new_config != config:
|
if new_config != config:
|
||||||
click.echo(
|
click.echo(
|
||||||
"WARNING configuration changed. Tutor web UI is now going to restart. Reload this page to continue."
|
"WARNING configuration changed. Tutor web UI is now going to restart. Reload this page to continue."
|
||||||
@ -77,7 +76,6 @@ def start(root, port, host):
|
|||||||
|
|
||||||
|
|
||||||
@click.command(help="Configure authentication")
|
@click.command(help="Configure authentication")
|
||||||
@opts.root
|
|
||||||
@click.option("-u", "--user", prompt="User name", help="Authentication user name")
|
@click.option("-u", "--user", prompt="User name", help="Authentication user name")
|
||||||
@click.option(
|
@click.option(
|
||||||
"-p",
|
"-p",
|
||||||
@ -87,12 +85,13 @@ def start(root, port, host):
|
|||||||
confirmation_prompt=True,
|
confirmation_prompt=True,
|
||||||
help="Authentication password",
|
help="Authentication password",
|
||||||
)
|
)
|
||||||
def configure(root, user, password):
|
@click.pass_obj
|
||||||
save_config(root, {"user": user, "password": password})
|
def configure(context, user, password):
|
||||||
|
save_config(context.root, {"user": user, "password": password})
|
||||||
fmt.echo_info(
|
fmt.echo_info(
|
||||||
"The web UI configuration has been updated. "
|
"The web UI configuration has been updated. "
|
||||||
"If at any point you wish to reset your username and password, "
|
"If at any point you wish to reset your username and password, "
|
||||||
"just delete the following file:\n\n {}".format(config_path(root))
|
"just delete the following file:\n\n {}".format(config_path(context.root))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,7 +185,9 @@ def check_existing_config(root):
|
|||||||
"""
|
"""
|
||||||
if not os.path.exists(config_path(root)):
|
if not os.path.exists(config_path(root)):
|
||||||
raise exceptions.TutorError(
|
raise exceptions.TutorError(
|
||||||
"Project root does not exist. Make sure to generate the initial configuration with `tutor config save --interactive` or `tutor config quickstart` prior to running other commands."
|
"Project root does not exist. Make sure to generate the initial "
|
||||||
|
"configuration with `tutor config save --interactive` or `tutor config "
|
||||||
|
"quickstart` prior to running other commands."
|
||||||
)
|
)
|
||||||
env.check_is_up_to_date(root)
|
env.check_is_up_to_date(root)
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import appdirs
|
|
||||||
import click
|
|
||||||
|
|
||||||
from . import serialize
|
|
||||||
|
|
||||||
|
|
||||||
root = click.option(
|
|
||||||
"-r",
|
|
||||||
"--root",
|
|
||||||
envvar="TUTOR_ROOT",
|
|
||||||
default=appdirs.user_data_dir(appname="tutor"),
|
|
||||||
show_default=True,
|
|
||||||
type=click.Path(resolve_path=True),
|
|
||||||
help="Root project directory (environment variable: TUTOR_ROOT)",
|
|
||||||
)
|
|
||||||
|
|
||||||
edx_platform_path = click.option(
|
|
||||||
"-P",
|
|
||||||
"--edx-platform-path",
|
|
||||||
envvar="TUTOR_EDX_PLATFORM_PATH",
|
|
||||||
type=click.Path(exists=True, dir_okay=True, resolve_path=True),
|
|
||||||
help="Mount a local edx-platform from the host (environment variable: TUTOR_EDX_PLATFORM_PATH)",
|
|
||||||
)
|
|
||||||
|
|
||||||
edx_platform_development_settings = click.option(
|
|
||||||
"-S",
|
|
||||||
"--edx-platform-settings",
|
|
||||||
envvar="TUTOR_EDX_PLATFORM_SETTINGS",
|
|
||||||
default="tutor.development",
|
|
||||||
help="Mount a local edx-platform from the host (environment variable: TUTOR_EDX_PLATFORM_PATH)",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class YamlParamType(click.ParamType):
|
|
||||||
name = "yaml"
|
|
||||||
|
|
||||||
def convert(self, value, param, ctx):
|
|
||||||
try:
|
|
||||||
k, v = value.split("=")
|
|
||||||
except ValueError:
|
|
||||||
self.fail("'{}' is not of the form 'key=value'.".format(value), param, ctx)
|
|
||||||
return k, serialize.parse(v)
|
|
@ -156,3 +156,15 @@ def iter_hooks(config, hook_name):
|
|||||||
|
|
||||||
def iter_templates(config):
|
def iter_templates(config):
|
||||||
yield from Plugins.instance(config).iter_templates()
|
yield from Plugins.instance(config).iter_templates()
|
||||||
|
|
||||||
|
|
||||||
|
def iter_commands():
|
||||||
|
"""
|
||||||
|
TODO doesn't this slow down the cli? (we need to import all plugins)
|
||||||
|
Also, do we really need the `config` argument? Do we want to make it possible for disabled plugins to be loaded?
|
||||||
|
TODO document this
|
||||||
|
"""
|
||||||
|
for plugin_name, plugin in iter_installed():
|
||||||
|
command = getattr(plugin, "command", None)
|
||||||
|
if command:
|
||||||
|
yield plugin_name, command
|
||||||
|
Loading…
Reference in New Issue
Block a user