Exemplo n.º 1
0
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='******'MYSQL_ROOT_PASSWORD']}'",
        ],
    )
    context.invoke(compose.stop)
Exemplo n.º 2
0
 def test_save_full(self) -> None:
     with tempfile.TemporaryDirectory() as root:
         config = tutor_config.load_full(root)
         with patch.object(fmt, "STDOUT"):
             env.save(root, config)
         self.assertTrue(
             os.path.exists(
                 os.path.join(root, "env", "local", "docker-compose.yml")))
Exemplo n.º 3
0
 def test_save_full_with_https(self):
     defaults = tutor_config.load_defaults()
     defaults["ACTIVATE_HTTPS"] = True
     with tempfile.TemporaryDirectory() as root:
         with unittest.mock.patch.object(fmt, "STDOUT"):
             env.save(root, defaults)
             with open(os.path.join(root, "env", "apps", "nginx", "lms.conf")) as f:
                 self.assertIn("ssl", f.read())
Exemplo n.º 4
0
 def test_save_full(self):
     defaults = tutor_config.load_defaults()
     with tempfile.TemporaryDirectory() as root:
         with unittest.mock.patch.object(fmt, "STDOUT"):
             env.save(root, defaults)
         self.assertTrue(os.path.exists(os.path.join(root, "env", "version")))
         self.assertTrue(
             os.path.exists(os.path.join(root, "env", "local", "docker-compose.yml"))
         )
Exemplo n.º 5
0
 def test_save_full_with_https(self):
     defaults = tutor_config.load_defaults()
     with tempfile.TemporaryDirectory() as root:
         config = tutor_config.load_current(root, defaults)
         tutor_config.merge(config, defaults)
         config["ENABLE_HTTPS"] = True
         with unittest.mock.patch.object(fmt, "STDOUT"):
             env.save(root, config)
             with open(os.path.join(root, "env", "apps", "caddy", "Caddyfile")) as f:
                 self.assertIn("www.myopenedx.com {", f.read())
Exemplo n.º 6
0
def upgrade_from_maple(context: Context, config: Config) -> None:
    fmt.echo_info("Upgrading from Maple")
    # The environment needs to be updated because the backpopulate/backfill commands are from Nutmeg
    tutor_env.save(context.root, config)

    # Start mysql
    k8s.kubectl_apply(
        context.root,
        "--selector",
        "app.kubernetes.io/name=mysql",
    )
    k8s.wait_for_pod_ready(config, "mysql")

    # lms upgrade
    k8s.kubectl_apply(
        context.root,
        "--selector",
        "app.kubernetes.io/name=lms",
    )
    k8s.wait_for_pod_ready(config, "lms")

    # Command backpopulate_user_tours
    k8s.kubectl_exec(config, "lms",
                     ["sh", "-e", "-c", "./manage.py lms migrate user_tours"])
    k8s.kubectl_exec(
        config, "lms",
        ["sh", "-e", "-c", "./manage.py lms backpopulate_user_tours"])

    # cms upgrade
    k8s.kubectl_apply(
        context.root,
        "--selector",
        "app.kubernetes.io/name=cms",
    )
    k8s.wait_for_pod_ready(config, "cms")

    # Command backfill_course_tabs
    k8s.kubectl_exec(
        config, "cms",
        ["sh", "-e", "-c", "./manage.py cms migrate contentstore"])
    k8s.kubectl_exec(
        config,
        "cms",
        ["sh", "-e", "-c", "./manage.py cms migrate split_modulestore_django"],
    )
    k8s.kubectl_exec(
        config, "cms",
        ["sh", "-e", "-c", "./manage.py cms backfill_course_tabs"])

    # Command simulate_publish
    k8s.kubectl_exec(
        config, "cms",
        ["sh", "-e", "-c", "./manage.py cms migrate course_overviews"])
    k8s.kubectl_exec(config, "cms",
                     ["sh", "-e", "-c", "./manage.py cms simulate_publish"])
Exemplo n.º 7
0
 def test_save_full_with_https(self) -> None:
     with temporary_root() as root:
         config = tutor_config.load_full(root)
         config["ENABLE_HTTPS"] = True
         with patch.object(fmt, "STDOUT"):
             env.save(root, config)
             with open(
                 os.path.join(env.base_dir(root), "apps", "caddy", "Caddyfile"),
                 encoding="utf-8",
             ) as f:
                 self.assertIn("www.myopenedx.com{$default_site_port}", f.read())
Exemplo n.º 8
0
def quickstart(context: click.Context, non_interactive: bool) -> None:
    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"))
        context.invoke(
            upgrade,
            from_release=tutor_env.get_env_release(context.obj.root),
        )

    click.echo(fmt.title("Interactive platform configuration"))
    config = tutor_config.load_minimal(context.obj.root)
    if not non_interactive:
        interactive_config.ask_questions(config, run_for_prod=True)
    tutor_config.save_config_file(context.obj.root, config)
    config = tutor_config.load_full(context.obj.root)
    tutor_env.save(context.obj.root, config)

    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 and push them to your private repository now by running the following
commands in a different shell:

    tutor images build all # add your custom images here
    tutor images push all

Press enter when you are ready to continue"""
        click.confirm(fmt.question(question),
                      default=True,
                      abort=True,
                      prompt_suffix=" ")

    click.echo(fmt.title("Starting the platform"))
    context.invoke(start)

    click.echo(fmt.title("Database creation and migrations"))
    context.invoke(init, limit=None)

    config = tutor_config.load(context.obj.root)
    fmt.echo_info(
        """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"],
        ))
Exemplo n.º 9
0
    def test_plugin_templates(self) -> None:
        with tempfile.TemporaryDirectory() as plugin_templates:
            DictPlugin({
                "name": "plugin1",
                "version": "0",
                "templates": plugin_templates
            })
            # Create two templates
            os.makedirs(os.path.join(plugin_templates, "plugin1", "apps"))
            with open(
                    os.path.join(plugin_templates, "plugin1",
                                 "unrendered.txt"),
                    "w",
                    encoding="utf-8",
            ) as f:
                f.write("This file should not be rendered")
            with open(
                    os.path.join(plugin_templates, "plugin1", "apps",
                                 "rendered.txt"),
                    "w",
                    encoding="utf-8",
            ) as f:
                f.write("Hello my ID is {{ ID }}")

            # Render templates
            with temporary_root() as root:
                # Create configuration
                config: Config = tutor_config.load_full(root)
                config["ID"] = "Hector Rumblethorpe"
                plugins.load("plugin1")
                tutor_config.save_enabled_plugins(config)

                # Render environment
                with patch.object(fmt, "STDOUT"):
                    env.save(root, config)

                # Check that plugin template was rendered
                root_env = os.path.join(root, "env")
                dst_unrendered = os.path.join(root_env, "plugins", "plugin1",
                                              "unrendered.txt")
                dst_rendered = os.path.join(root_env, "plugins", "plugin1",
                                            "apps", "rendered.txt")
                self.assertFalse(os.path.exists(dst_unrendered))
                self.assertTrue(os.path.exists(dst_rendered))
                with open(dst_rendered, encoding="utf-8") as f:
                    self.assertEqual("Hello my ID is Hector Rumblethorpe",
                                     f.read())
Exemplo n.º 10
0
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"""
        )

    click.echo(fmt.title("Interactive platform configuration"))
    config = tutor_config.load_minimal(context.obj.root)
    if not non_interactive:
        interactive_config.ask_questions(config, run_for_prod=False)
    tutor_config.save_config_file(context.obj.root, config)
    config = tutor_config.load_full(context.obj.root)
    tutor_env.save(context.obj.root, config)

    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("Building Docker image for LMS and CMS development"))
    context.invoke(compose.dc_command, command="build", args=["lms"])

    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)

    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}:8000
    {http}://{cms_host}:8001
    """.format(
            http="https" if config["ENABLE_HTTPS"] else "http",
            lms_host=config["LMS_HOST"],
            cms_host=config["CMS_HOST"],
        )
    )
Exemplo n.º 11
0
def upgrade_from_maple(context: click.Context, config: Config) -> None:
    click.echo(fmt.title("Upgrading from Maple"))
    # The environment needs to be updated because the management commands are from Nutmeg
    tutor_env.save(context.obj.root, config)
    # Command backpopulate_user_tours
    context.invoke(
        compose.run,
        args=["lms", "sh", "-e", "-c", "./manage.py lms migrate user_tours"],
    )
    context.invoke(
        compose.run,
        args=[
            "lms", "sh", "-e", "-c", "./manage.py lms backpopulate_user_tours"
        ],
    )
    # Command backfill_course_tabs
    context.invoke(
        compose.run,
        args=["cms", "sh", "-e", "-c", "./manage.py cms migrate contentstore"],
    )
    context.invoke(
        compose.run,
        args=[
            "cms",
            "sh",
            "-e",
            "-c",
            "./manage.py cms migrate split_modulestore_django",
        ],
    )
    context.invoke(
        compose.run,
        args=["cms", "sh", "-e", "-c", "./manage.py cms backfill_course_tabs"],
    )
    # Command simulate_publish
    context.invoke(
        compose.run,
        args=[
            "cms", "sh", "-e", "-c", "./manage.py cms migrate course_overviews"
        ],
    )
    context.invoke(
        compose.run,
        args=["cms", "sh", "-e", "-c", "./manage.py cms simulate_publish"],
    )
Exemplo n.º 12
0
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 MongoDB (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)
Exemplo n.º 13
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)
Exemplo n.º 14
0
def quickstart(
    context: click.Context,
    mounts: t.Tuple[t.List[compose.MountParam.MountType]],
    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"))
    config = tutor_config.load_minimal(context.obj.root)
    if not non_interactive:
        interactive_config.ask_questions(config)
    tutor_config.save_config_file(context.obj.root, config)
    config = tutor_config.load_full(context.obj.root)
    tutor_env.save(context.obj.root, config)

    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, mounts=mounts, detach=True)
    click.echo(fmt.title("Database creation and migrations"))
    context.invoke(compose.init, mounts=mounts)

    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"],
    ))