v10.0.0 Upgrade to Juniper (2020-06-15)

Here, we upgrade the Open edX platform from Ironwood to Juniper. This
upgrade does not come with many feature changes, but there are many
technical improvements under the hood:

- Upgrade from Python 2.7 to 3.5
- Upgrade from Mongodb v3.2 to v3.6
- Upgrade Ruby to 2.5.7

We took the opportunity to completely rething the way locally running
platforms should be accessed for testing purposes. It is no longer
possible to access a running platform from http://localhost and
http://studio.localhost. Instead, users should access
http://local.overhang.io and https://studio.local.overhang.io. This
drastically simplifies internal communication between Docker containers.

To upgrade, users should simply run:

    tutor local quickstart

For Kubernetes platform, the upgrade process is outlined when running:

    tutor k8s upgrade --from=ironwood
This commit is contained in:
Régis Behmo 2019-12-24 17:22:12 +01:00
parent 5bbc196259
commit 4d6de0138a
54 changed files with 537 additions and 213 deletions

View File

@ -2,10 +2,11 @@
Note: Breaking changes between versions are indicated by "💥".
## Unreleased
## v10.0.0 (2020-06-15)
- 💥[Improvement] Upgrade to Juniper 🍾
- [Bugfix] Fix nginx resolver address to address container restarts
- [Feature] Add `--limit=myplugin` option to `init` commands for limit execution of initialisation to certain services and plugins
- [Feature] Add `--limit=myplugin` option to `init` commands to limit execution of initialisation to certain services and plugins
## v3.12.6 (2020-06-01)

View File

@ -88,7 +88,8 @@ ci-bundle: bundle ## Create bundle and run basic tests
yes "" | ./dist/tutor config save --interactive
./dist/tutor config save
./dist/tutor plugins list
./dist/tutor plugins enable discovery ecommerce figures lts minio notes xqueue
# ./dist/tutor plugins enable discovery ecommerce figures lts minio notes xqueue
./dist/tutor plugins enable discovery ecommerce lts minio notes xqueue
./dist/tutor plugins list
./dist/tutor lts --help

View File

@ -5,7 +5,7 @@ from tutor.plugins import OfficialPlugin
for plugin_name in [
"discovery",
"ecommerce",
"figures",
# "figures",
"lts",
"minio",
"notes",

View File

@ -207,7 +207,7 @@ openedx Docker Image build arguments
When building the "openedx" Docker image, it is possible to specify a few `arguments <https://docs.docker.com/engine/reference/builder/#arg>`__:
- ``EDX_PLATFORM_REPOSITORY`` (default: ``"https://github.com/edx/edx-platform.git"``)
- ``EDX_PLATFORM_VERSION`` (default: ``"open-release/ironwood.master"``)
- ``EDX_PLATFORM_VERSION`` (default: ``"open-release/juniper.1"``)
- ``EDX_PLATFORM_VERSION_DATE`` (default: ``"20200227"``)
- ``NPM_REGISTRY`` (default: ``"https://registry.npmjs.org/"``)
@ -269,14 +269,14 @@ You may want to run your own flavor of edx-platform instead of the `official ver
--build-arg EDX_PLATFORM_REPOSITORY=https://mygitrepo/edx-platform.git \
--build-arg EDX_PLATFORM_VERSION=my-tag-or-branch
Note that your release must be a fork of Ironwood in order to work. Otherwise, you may have important compatibility issues with other services. In particular, **don't try to run Tutor with older versions of Open edX**.
Note that your release must be a fork of the Juniper release in order to work. Otherwise, you may have important compatibility issues with other services. In particular, **don't try to run Tutor with older versions of Open edX**.
.. _i18n:
Adding custom translations
~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are not running Open edX in English, chances are that some strings will not be properly translated. In most cases, this is because not enough contributors have helped translate Open edX in your language. It happens! With Tutor, available translated languages include those that come bundled with `edx-platform <https://github.com/edx/edx-platform/tree/open-release/ironwood.master/conf/locale>`__ as well as those from `openedx-i18n <https://github.com/openedx/openedx-i18n/tree/master/edx-platform/locale>`__.
If you are not running Open edX in English, chances are that some strings will not be properly translated. In most cases, this is because not enough contributors have helped translate Open edX in your language. It happens! With Tutor, available translated languages include those that come bundled with `edx-platform <https://github.com/edx/edx-platform/tree/open-release/juniper.1/conf/locale>`__ as well as those from `openedx-i18n <https://github.com/openedx/openedx-i18n/tree/master/edx-platform/locale>`__.
Tutor offers a relatively simple mechanism to add custom translations to the openedx Docker image. You should create a folder that corresponds to your language code in the "build/openedx/locale" folder of the Tutor environment. This folder should contain a "LC_MESSAGES" folder. For instance::
@ -289,9 +289,9 @@ Then, add a "django.po" file there that will contain your custom translations::
msgid "String to translate"
msgstr "你翻译的东西 la traduction de votre bidule"
The "String to translate" part should match *exactly* the string that you would like to translate. You cannot make it up! The best way to find this string is to copy-paste it from the `upstream django.po file for the English language <https://github.com/edx/edx-platform/blob/open-release/ironwood.master/conf/locale/en/LC_MESSAGES/django.po>`__.
The "String to translate" part should match *exactly* the string that you would like to translate. You cannot make it up! The best way to find this string is to copy-paste it from the `upstream django.po file for the English language <https://github.com/edx/edx-platform/blob/open-release/juniper.1/conf/locale/en/LC_MESSAGES/django.po>`__.
If you cannot find the string to translate in this file, then it means that you are trying to translate a string that is used in some piece of javascript code. Those strings are stored in a different file named "djangojs.po". You can check it out `in the edx-platform repo as well <https://github.com/edx/edx-platform/blob/open-release/ironwood.master/conf/locale/en/LC_MESSAGES/djangojs.po>`__. Your custom javascript strings should also be stored in a "djangojs.po" file that should be placed in the same directory.
If you cannot find the string to translate in this file, then it means that you are trying to translate a string that is used in some piece of javascript code. Those strings are stored in a different file named "djangojs.po". You can check it out `in the edx-platform repo as well <https://github.com/edx/edx-platform/blob/open-release/juniper.1/conf/locale/en/LC_MESSAGES/djangojs.po>`__. Your custom javascript strings should also be stored in a "djangojs.po" file that should be placed in the same directory.
To recap, here is an example. To translate a few strings in French, both from django.po and djangojs.po, we would have the following file hierarchy::

View File

@ -9,7 +9,9 @@ The following commands assume you have previously launched a :ref:`local <local>
tutor local quickstart
You should setup real host names for the LMS and the CMS (i.e: not "localhost"). It is not necessary to configure the DNS records for local development: in other words, it doesn't matter that the chosen domain names exist or not for your platform, as the LMS and the CMS will be accessed on ``localhost``. You should *not* activate HTTPS certificates, which will not work locally.
In order to run the platform in development mode, you **must** answer no ("n") to the question "Are you configuring a production platform".
Note that the local.overhang.io `domain <https://dnschecker.org/#A/local.overhang.io>`__ and its `subdomains <https://dnschecker.org/#CNAME/studio.local.overhang.io>`__ all point to 127.0.0.1. This is just a domain name that was setup to conveniently access a locally running Open edX platform.
Once the local platform has been configured, you should stop it so that it does not interfere with the development environment::
@ -23,7 +25,7 @@ This ``openedx-dev`` development image differs from the ``openedx`` production i
- The user that runs inside the container has the same UID as the user on the host, in order to avoid permission problems inside mounted volumes (and in particular in the edx-platform repository).
- Additional python and system requirements are installed for convenient debugging: `ipython <https://ipython.org/>`__, `ipdb <https://pypi.org/project/ipdb/>`__, vim, telnet.
- The edx-platform `development requirements <https://github.com/edx/edx-platform/blob/open-release/ironwood.master/requirements/edx/development.in>`__ are installed.
- The edx-platform `development requirements <https://github.com/edx/edx-platform/blob/open-release/juniper.1/requirements/edx/development.in>`__ are installed.
Since the ``openedx-dev`` is based upon the ``openedx`` docker image, it should be re-built every time the ``openedx`` docker image is modified.
@ -32,8 +34,8 @@ Run a local development webserver
::
tutor dev runserver lms # Access the lms at http://localhost:8000
tutor dev runserver cms # Access the cms at http://localhost:8001
tutor dev runserver lms # Access the lms at http://local.overhang.io:8000
tutor dev runserver cms # Access the cms at http://studio.local.overhang.io:8001
Running arbitrary commands
--------------------------
@ -50,10 +52,9 @@ To open a python shell in the LMS or CMS, run::
You can then import edx-platform and django modules and execute python code.
To collect assets, you can use the standard ``update_assets`` Open edX command with the right settings::
To collect assets, you can use the ``openedx-assets`` command that ships with Tutor::
tutor dev run lms paver update_assets --settings=tutor.development
tutor dev run cms paver update_assets --settings=tutor.development
tutor dev run openedx-assets --env=dev
Point to a local edx-platform
-----------------------------
@ -89,7 +90,7 @@ Then, add the following content::
This override file will be loaded when running any ``tutor dev ..`` command. The edx-platform repo mounted at the specified path will be automaticall mounted inside all LMS and CMS containers. With this file, you should no longer specify the ``-v`` option from the command line with the ``run`` or ``runserver`` commands.
**Note:** containers are built on the Ironwood release. If you are working on a different version of Open edX, you will have to rebuild the ``openedx`` docker images with the version. See the :ref:`fork edx-platform section <edx_platform_fork>`.
**Note:** containers are built on the Juniper release. If you are working on a different version of Open edX, you will have to rebuild the ``openedx`` docker images with the version. See the :ref:`fork edx-platform section <edx_platform_fork>`.
Prepare the edx-platform repo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -145,9 +146,9 @@ Then, run a local webserver::
tutor dev runserver lms
The LMS can then be accessed at http://localhost:8000. You will then have to :ref:`enable that theme <settheme>` for the development domain names::
The LMS can then be accessed at http://local.overhang.io:8000. You will then have to :ref:`enable that theme <settheme>` for the development domain names::
tutor dev settheme mythemename localhost:8000 localhost:8001
tutor dev settheme mythemename local.overhang.io:8000 studio.local.overhang.io:8001
Re-build development docker image (and compile assets)::
@ -157,7 +158,7 @@ Watch the themes folders for changes (in a different terminal)::
tutor dev run watchthemes
Make changes to some of the files inside the theme directory: the theme assets should be automatically recompiled and visible at http://localhost:8000.
Make changes to some of the files inside the theme directory: the theme assets should be automatically recompiled and visible at http://local.overhang.io:8000.
Custom edx-platform settings
----------------------------

View File

@ -39,18 +39,24 @@ This is the simplest and recommended installation method for most people. Note h
.. _install_source:
From source
-----------
Alternative installation methods
--------------------------------
If you would like to inspect the Tutor source code, you are most welcome to install Tutor from `Pypi <https://pypi.org/project/tutor-openedx/>`_ or directly from `the Github repository <https://github.com/overhangio/tutor>`_. You will need python >= 3.6 and the libyaml development headers. On Ubuntu, these requirements can be installed by running::
sudo apt install python3 libyaml-dev
Installing from pypi::
Installing from pypi
~~~~~~~~~~~~~~~~~~~~
::
pip install tutor-openedx
Installing from a local clone of the repository::
Installing from source
~~~~~~~~~~~~~~~~~~~~~~
::
git clone https://github.com/overhangio/tutor
cd tutor
@ -59,7 +65,7 @@ Installing from a local clone of the repository::
.. _cloud_install:
Zero-click AWS installation
---------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tutor can be launched on Amazon Web Services very quickly with the `official Tutor AMI <https://aws.amazon.com/marketplace/pp/B07PV3TB8X>`__. Shell access is not required, as all configuration will happen through the Tutor web user interface. For detailed installation instructions, we recommend watching the following video:

View File

@ -47,7 +47,7 @@ On Minikube, run::
minikube addons enable ingress
With Kubernetes, your Open edX platform will *not* be available at localhost or studio.localhost. Instead, you will have to access your platform with the domain names you specified for the LMS and the CMS. To do so on a local computer, you will need to add the following line to /etc/hosts::
With Kubernetes, your Open edX platform will *not* be available at localhost. Instead, you will have to access your platform with the domain names you specified for the LMS and the CMS. To do so on a local computer, you will need to add the following line to /etc/hosts::
MINIKUBEIP yourdomain.com studio.yourdomain.com preview.yourdomain.com notes.yourdomain.com

View File

@ -12,6 +12,9 @@ In the following, environment and data files will be generated in a user-specifi
export TUTOR_ROOT=/path/to/tutorroot
tutor run ...
.. note::
As of v10.0.0, a locally-running Open edX platform can no longer be accessed from http://localhost or http://studio.localhost. Instead, when running ``tutor local quickstart``, you must now decide whether you are running a platform that will be used in production. If not, the platform will be automatically be bound to http://local.overhang.io and http://studio.local.overhang.io, which are domain names that point to 127.0.0.1 (localhost). This change was made to facilitate internal communication between Docker containers.
Main commands
-------------
@ -55,7 +58,7 @@ Running Open edX
tutor local start
This will launch the various docker containers required for your Open edX platform. The LMS and the Studio will then be reachable at the domain name you specified during the configuration step. You can also access them at http://localhost and http://studio.localhost.
This will launch the various docker containers required for your Open edX platform. The LMS and the Studio will then be reachable at the domain name you specified during the configuration step.
To stop the running containers, just hit Ctrl+C.
@ -124,12 +127,9 @@ Setting a new theme
The default Open edX theme is rather bland, so Tutor makes it easy to switch to a different theme::
tutor local settheme mytheme localhost
tutor local settheme mytheme $(tutor config printvalue LMS_HOST) $(tutor config printvalue CMS_HOST)
Notice the "localhost" argument: in Open edX, themes are assigned per domain name. That means that your custom theme will only be visible if you access your platform at http://localhost. So you might want to run this command with all possible domain names. For instance, to assign your custom theme both to the LMS and the studio, locally and in production, run::
tutor local settheme mytheme localhost studio.localhost \
$(tutor config printvalue LMS_HOST) $(tutor config printvalue CMS_HOST)
Notice that we pass the hostnames of the LMS and the CMS to the ``settheme`` command: this is because in Open edX, themes are assigned per domain name.
Out of the box, only the default "open-edx" theme is available. We also developed `Indigo, a beautiful, customizable theme <https://github.com/overhangio/indigo>`__ which is easy to install with Tutor.

View File

@ -10,7 +10,7 @@ The ``podman`` CLI aims to be fully compatible with the ``docker`` CLI, and ``po
Enabling Podman
^^^^^^^^^^^^^^^
~~~~~~~~~~~~~~~
Podman is supported on a variety of development platforms, see the `installation instructions <https://podman.io/getting-started/installation>`_ for details.
@ -26,7 +26,7 @@ Once you have installed Podman and its dependencies on the platform of your choi
Enabling podman-compose
^^^^^^^^^^^^^^^^^^^^^^^
~~~~~~~~~~~~~~~~~~~~~~~
``podman-compose`` is available as a package from PyPI, and can thus be installed with ``pip``. See `its README <https://github.com/containers/podman-compose/blob/devel/README.md>`_ for installation instructions. Note that if you have installed Tutor in its own virtualenv, you'll need to run ``pip install podman-compose`` in that same virtualenv.
@ -42,7 +42,7 @@ Once installed, you'll again need to create a symbolic link that aliases ``docke
Verifying your environment
^^^^^^^^^^^^^^^^^^^^^^^^^^
~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you have configured your symbolic links as described, you should be able to run ``docker version`` and ``docker-compose --help`` and their output should agree, respectively, with ``podman version`` and ``podman-compose --help``.

View File

@ -18,7 +18,7 @@ Yes :) This is what happens when you run ``tutor local quickstart``:
2. Configuration files are generated from templates.
3. Docker images are downloaded.
4. Docker containers are provisioned.
5. A full, production-ready Open edX platform (`Ironwood <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/open-release-ironwood.master/platform_releases/ironwood.html#open-edx-ironwood-release>`__ release) is run with docker-compose.
5. A full, production-ready Open edX platform (`Juniper <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/open-release-juniper.master/platform_releases/juniper.html>`__ release) is run with docker-compose.
The whole procedure should require less than 10 minutes, on a server with a good bandwidth. Note that your host environment will not be affected in any way, since everything runs inside docker containers. Root access is not even necessary.

View File

@ -1,7 +1,7 @@
User testimonials
-----------------
*"Tutor simplifies the deployment of Open edX, so that we can directly focus on customizing our platform. Thanks a lot." -- Shashi Kiran G M
*"Tutor simplifies the deployment of Open edX, so that we can directly focus on customizing our platform. Thanks a lot."* -- Shashi Kiran G M
*"Tutor is an absolutely fabulous initiative"* -- Lionel Meinertzhagen, Learning engineer - `Université Libre de Bruxelles <https://www.ulb.be/>`__

View File

@ -101,11 +101,6 @@ The error produced should help you better understand what is happening.
This will occur if you try to run a development environment without patching the LOGGING configuration, as indicated in the `development_` section. Maybe you correctly patched the development settings, but they are not taken into account? For instance, you might have correctly defined the ``EDX_PLATFORM_SETTINGS`` environment variable, but ``paver`` uses the ``devstack`` settings (which does not patch the ``LOGGING`` variable). This is because calling ``paver lms --settings=development`` or ``paver cms --settings=development`` ignores the ``--settings`` argument. Yes, it might be considered an edx-platform bug... Instead, you should run the ``update_assets`` and ``runserver`` commands, as explained above.
"TypeError: get_logger_config() got an unexpected keyword argument 'debug'"
-------------------------------------------------------------------------------
This might occur when you try to run the latest version of ``edx-platform``, and not a version close to ``ironwood.master``. It is no longer necessary to patch the ``LOGGING`` configuration in the latest ``edx-platform`` releases, as indicated in the `development_` section, so you should remove the call to ``get_logger_config`` altogether from your development settings.
The chosen default language does not display properly
-----------------------------------------------------

View File

@ -1,6 +1,7 @@
appdirs
click>=7.0
click_repl
pycryptodome
jinja2>=2.9
kubernetes
pyyaml>=4.2b1

View File

@ -10,15 +10,16 @@ certifi==2020.4.5.1 # via kubernetes, requests
chardet==3.0.4 # via requests
click-repl==0.1.6
click==7.1.1
google-auth==1.14.0 # via kubernetes
google-auth==1.14.1 # via kubernetes
idna==2.9 # via requests
jinja2==2.11.1
jinja2==2.11.2
kubernetes==11.0.0
markupsafe==1.1.1 # via jinja2
oauthlib==3.1.0 # via requests-oauthlib
prompt-toolkit==3.0.5 # via click-repl
pyasn1-modules==0.2.8 # via google-auth
pyasn1==0.4.8 # via pyasn1-modules, rsa
pycryptodome==3.9.7
python-dateutil==2.8.1 # via kubernetes
pyyaml==5.3.1
requests-oauthlib==1.3.0 # via kubernetes

View File

@ -16,34 +16,35 @@ cffi==1.14.0 # via cryptography
chardet==3.0.4
click-repl==0.1.6
click==7.1.1
cryptography==2.8 # via secretstorage
cryptography==2.9.2 # via secretstorage
docutils==0.16 # via readme-renderer
google-auth==1.14.0
google-auth==1.14.1
idna==2.9
importlib-metadata==1.6.0 # via keyring, twine
isort==4.3.21 # via pylint
jeepney==0.4.3 # via keyring, secretstorage
jinja2==2.11.1
jinja2==2.11.2
keyring==21.2.0 # via twine
kubernetes==11.0.0
lazy-object-proxy==1.4.3 # via astroid
markupsafe==1.1.1
mccabe==0.6.1 # via pylint
oauthlib==3.1.0
pathspec==0.7.0 # via black
pip-tools==4.5.1
pathspec==0.8.0 # via black
pip-tools==5.0.0
pkginfo==1.5.0.1 # via twine
prompt-toolkit==3.0.5
pyasn1-modules==0.2.8
pyasn1==0.4.8
pycparser==2.20 # via cffi
pycryptodome==3.9.7
pygments==2.6.1 # via readme-renderer
pyinstaller==3.6
pylint==2.4.4
python-dateutil==2.8.1
pyyaml==5.3.1
readme-renderer==25.0 # via twine
regex==2020.2.20 # via black
readme-renderer==26.0 # via twine
regex==2020.4.4 # via black
requests-oauthlib==1.3.0
requests-toolbelt==0.9.1 # via twine
requests==2.23.0
@ -51,7 +52,7 @@ rsa==4.0
secretstorage==3.1.2 # via keyring
six==1.14.0
toml==0.10.0 # via black
tqdm==4.44.1 # via twine
tqdm==4.45.0 # via twine
twine==3.1.1
typed-ast==1.4.1 # via astroid, black
urllib3==1.25.9
@ -62,4 +63,5 @@ wrapt==1.11.2 # via astroid
zipp==3.1.0 # via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
# pip
# setuptools

View File

@ -13,10 +13,10 @@ chardet==3.0.4
click-repl==0.1.6
click==7.1.1
docutils==0.16 # via sphinx
google-auth==1.14.0
google-auth==1.14.1
idna==2.9
imagesize==1.2.0 # via sphinx
jinja2==2.11.1
jinja2==2.11.2
kubernetes==11.0.0
markupsafe==1.1.1
oauthlib==3.1.0
@ -24,8 +24,9 @@ packaging==20.3 # via sphinx
prompt-toolkit==3.0.5
pyasn1-modules==0.2.8
pyasn1==0.4.8
pycryptodome==3.9.7
pygments==2.6.1 # via sphinx
pyparsing==2.4.6 # via packaging
pyparsing==2.4.7 # via packaging
python-dateutil==2.8.1
pytz==2019.3 # via babel
pyyaml==5.3.1
@ -35,7 +36,7 @@ rsa==4.0
six==1.14.0
snowballstemmer==2.0.0 # via sphinx
sphinx-rtd-theme==0.4.3
sphinx==2.4.4
sphinx==3.0.3
sphinxcontrib-applehelp==1.0.2 # via sphinx
sphinxcontrib-devhelp==1.0.2 # via sphinx
sphinxcontrib-htmlhelp==1.0.3 # via sphinx

View File

@ -1,6 +1,6 @@
tutor-discovery
tutor-ecommerce
tutor-figures
#tutor-figures
tutor-lts
tutor-minio
tutor-notes

View File

@ -72,19 +72,22 @@ class EnvTests(unittest.TestCase):
def test_save_full(self):
defaults = tutor_config.load_defaults()
with tempfile.TemporaryDirectory() as root:
config = tutor_config.load_current(root, defaults)
tutor_config.merge(config, defaults)
with unittest.mock.patch.object(fmt, "STDOUT"):
env.save(root, defaults)
self.assertTrue(os.path.exists(os.path.join(root, "env", "version")))
env.save(root, config)
self.assertTrue(
os.path.exists(os.path.join(root, "env", "local", "docker-compose.yml"))
)
def test_save_full_with_https(self):
defaults = tutor_config.load_defaults()
defaults["ACTIVATE_HTTPS"] = True
with tempfile.TemporaryDirectory() as root:
config = tutor_config.load_current(root, defaults)
tutor_config.merge(config, defaults)
config["ACTIVATE_HTTPS"] = True
with unittest.mock.patch.object(fmt, "STDOUT"):
env.save(root, defaults)
env.save(root, config)
with open(os.path.join(root, "env", "apps", "nginx", "lms.conf")) as f:
self.assertIn("ssl", f.read())

View File

@ -1 +1 @@
__version__ = "3.12.6"
__version__ = "10.0.0"

View File

@ -4,6 +4,7 @@ import click
from . import compose
from .context import Context
from .. import config as tutor_config
from .. import env as tutor_env
from .. import fmt
from .. import utils
@ -15,7 +16,7 @@ class DevContext(Context):
args = []
for folder in ["local", "dev"]:
# Add docker-compose.yml and docker-compose.override.yml (if it exists)
# from "local" and "dev" folders
# from "local" and "dev" folders (but not docker-compose.prod.yml)
args += [
"-f",
tutor_env.pathjoin(root, folder, "docker-compose.yml"),
@ -41,12 +42,15 @@ def dev(context):
)
@click.argument("options", nargs=-1, required=False)
@click.argument("service")
def runserver(options, service):
@click.pass_obj
def runserver(context, options, service):
config = tutor_config.load(context.root)
if service in ["lms", "cms"]:
port = 8000 if service == "lms" else 8001
host = config["LMS_HOST"] if service == "lms" else config["CMS_HOST"]
fmt.echo_info(
"The {} service will be available at http://localhost:{}".format(
service, port
"The {} service will be available at http://{}:{}".format(
service, host, port
)
)
args = ["--service-ports", *options, service]

View File

@ -201,6 +201,40 @@ def logs(context, container, follow, tail, service):
utils.kubectl(*command)
@click.command(help="Upgrade from a previous Open edX named release")
@click.option(
"--from", "from_version", default="ironwood", type=click.Choice(["ironwood"])
)
@click.pass_obj
def upgrade(context, from_version):
config = tutor_config.load(context.root)
if from_version == "ironwood":
if not config["ACTIVATE_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (ACTIVATE_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v3.6. There is "
"nothing left to do."
)
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)
class K8sClients:
_instance = None
@ -392,3 +426,4 @@ k8s.add_command(importdemocourse)
k8s.add_command(settheme)
k8s.add_command(exec_command)
k8s.add_command(logs)
k8s.add_command(upgrade)

View File

@ -23,6 +23,8 @@ class LocalContext(Context):
return utils.docker_compose(
"-f",
tutor_env.pathjoin(root, "local", "docker-compose.yml"),
"-f",
tutor_env.pathjoin(root, "local", "docker-compose.prod.yml"),
*args,
"--project-name",
config["LOCAL_PROJECT_NAME"],
@ -43,6 +45,13 @@ def local(context):
)
@click.pass_obj
def quickstart(context, non_interactive, pullimages_):
if tutor_env.needs_major_upgrade(context.root):
click.echo(fmt.title("Upgrading from an older release"))
upgrade.callback(
from_version=tutor_env.current_release(context.root),
non_interactive=non_interactive,
)
click.echo(fmt.title("Interactive platform configuration"))
config = interactive_config.update(context.root, interactive=(not non_interactive))
click.echo(fmt.title("Updating the current environment"))
@ -58,22 +67,17 @@ def quickstart(context, non_interactive, pullimages_):
compose.start.callback(True, [])
click.echo(fmt.title("Database creation and migrations"))
compose.init.callback(limit=None)
echo_platform_info(config)
def echo_platform_info(config):
fmt.echo_info("The Open edX platform is now running in detached mode")
http = "https" if config["ACTIVATE_HTTPS"] else "http"
urls = []
if not config["ACTIVATE_HTTPS"] and not config["WEB_PROXY"]:
urls += ["http://localhost", "http://studio.localhost"]
urls.append("{http}://{lms_host}".format(http=http, lms_host=config["LMS_HOST"]))
urls.append("{http}://{cms_host}".format(http=http, cms_host=config["CMS_HOST"]))
fmt.echo_info(
"""Your Open edX platform is ready and can be accessed at the following urls:
"""The Open edX platform is now running in detached mode
Your Open edX platform is ready and can be accessed at the following urls:
{}""".format(
"\n ".join(urls)
{http}://{lms_host}
{http}://{cms_host}
""".format(
http="https" if config["ACTIVATE_HTTPS"] else "http",
lms_host=config["LMS_HOST"],
cms_host=config["CMS_HOST"],
)
)
@ -157,8 +161,81 @@ See the official certbot documentation for your platform: https://certbot.eff.or
utils.docker_run(*docker_run)
@click.command(help="Upgrade from a previous Open edX named release")
@click.option(
"--from", "from_version", default="ironwood", type=click.Choice(["ironwood"])
)
@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively")
@click.pass_obj
def upgrade(context, from_version, non_interactive):
config = tutor_config.load_no_check(context.root)
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=" "
)
if from_version == "ironwood":
upgrade_from_ironwood(context, config)
def upgrade_from_ironwood(context, config):
click.echo(fmt.title("Upgrading from Ironwood"))
tutor_env.save(context.root, config)
click.echo(fmt.title("Stopping any existing platform"))
compose.stop.callback([])
if not config["ACTIVATE_MONGODB"]:
fmt.echo_info(
"You are not running MongDB (ACTIVATE_MONGODB=false). It is your "
"responsibility to upgrade your MongoDb instance to v3.6. There is "
"nothing left to do."
)
return
# Note that the DOCKER_IMAGE_MONGODB value is never saved, because we only save the
# environment, not the configuration.
click.echo(fmt.title("Upgrading MongoDb from v3.2 to v3.4"))
config["DOCKER_IMAGE_MONGODB"] = "mongo:3.4.24"
tutor_env.save(context.root, config)
compose.start.callback(detach=True, services=["mongodb"])
compose.execute.callback(
[
"mongodb",
"mongo",
"--eval",
'db.adminCommand({ setFeatureCompatibilityVersion: "3.4" })',
]
)
compose.stop.callback([])
click.echo(fmt.title("Upgrading MongoDb from v3.4 to v3.6"))
config["DOCKER_IMAGE_MONGODB"] = "mongo:3.6.18"
tutor_env.save(context.root, config)
compose.start.callback(detach=True, services=["mongodb"])
compose.execute.callback(
[
"mongodb",
"mongo",
"--eval",
'db.adminCommand({ setFeatureCompatibilityVersion: "3.6" })',
]
)
compose.stop.callback([])
https.add_command(https_create)
https.add_command(https_renew)
local.add_command(https)
local.add_command(quickstart)
local.add_command(upgrade)
compose.add_commands(local)

View File

@ -24,6 +24,10 @@ def load(root):
configuration in the project root.
"""
check_existing_config(root)
return load_no_check(root)
def load_no_check(root):
config, defaults = load_all(root)
merge(config, defaults)
return config
@ -101,6 +105,7 @@ def load_required(config, defaults):
"OPENEDX_MYSQL_PASSWORD",
"ANDROID_OAUTH2_SECRET",
"ID",
"JWT_RSA_PRIVATE_KEY",
]:
if key not in config:
config[key] = env.render_unknown(config, defaults[key])

View File

@ -51,10 +51,13 @@ class Renderer:
environment.filters["common_domain"] = utils.common_domain
environment.filters["encrypt"] = utils.encrypt
environment.filters["list_if"] = utils.list_if
environment.filters["long_to_base64"] = utils.long_to_base64
environment.filters["random_string"] = utils.random_string
environment.filters["reverse_host"] = utils.reverse_host
environment.filters["rsa_private_key"] = utils.rsa_private_key
environment.filters["walk_templates"] = self.walk_templates
environment.globals["patch"] = self.patch
environment.globals["rsa_import_key"] = utils.rsa_import_key
environment.globals["TUTOR_VERSION"] = __version__
self.environment = environment
@ -293,14 +296,30 @@ def is_up_to_date(root):
return current_version(root) == __version__
def needs_major_upgrade(root):
"""
Return the current version as a tuple of int. E.g: (1, 0, 2).
"""
current = int(current_version(root).split(".")[0])
required = int(__version__.split(".")[0])
return current < required
def current_release(root):
"""
Return the name of the current Open edX release.
"""
return {"3": "ironwood", "10": "juniper"}[current_version(root).split(".")[0]]
def current_version(root):
"""
Return the current environment version. If the current environment has no version,
return "0".
return "0.0.0".
"""
path = pathjoin(root, VERSION_FILENAME)
if not os.path.exists(path):
return "0"
return "0.0.0"
return open(path).read().strip()

View File

@ -1,9 +1,8 @@
import os
import click
from . import config as tutor_config
from . import env
from . import exceptions
from . import fmt
from .__about__ import __version__
@ -22,16 +21,41 @@ def load_all(root, interactive=True):
"""
Load configuration and interactively ask questions to collect param values from the user.
"""
defaults = tutor_config.load_defaults()
config = tutor_config.load_current(root, defaults)
config, defaults = tutor_config.load_all(root)
if interactive:
ask_questions(config, defaults)
return config, defaults
def ask_questions(config, defaults):
ask("Your website domain name for students (LMS)", "LMS_HOST", config, defaults)
ask("Your website domain name for teachers (CMS)", "CMS_HOST", config, defaults)
run_for_prod = click.confirm(
fmt.question(
"Are you configuring a production platform? Type 'n' if you are just testing Tutor on your local computer"
),
prompt_suffix=" ",
default=True,
)
if not run_for_prod:
dev_values = {
"LMS_HOST": "local.overhang.io",
"CMS_HOST": "studio.local.overhang.io",
"ACTIVATE_HTTPS": False,
}
fmt.echo_info(
"""As you are not running this platform in production, we automatically set the following configuration values:"""
)
for k, v in dev_values.items():
config[k] = v
fmt.echo_info(" {} = {}".format(k, v))
if run_for_prod:
ask("Your website domain name for students (LMS)", "LMS_HOST", config, defaults)
if "localhost" in config["LMS_HOST"]:
raise exceptions.TutorError(
"You may not use 'localhost' as the LMS domain name. To run a local platform for testing purposes you should answer 'n' to the previous question."
)
ask("Your website domain name for teachers (CMS)", "CMS_HOST", config, defaults)
ask("Your platform name/title", "PLATFORM_NAME", config, defaults)
ask("Your public contact email address", "CONTACT_EMAIL", config, defaults)
ask_choice(
@ -119,15 +143,16 @@ def ask_questions(config, defaults):
"zh-tw",
],
)
ask_bool(
(
"Activate SSL/TLS certificates for HTTPS access? Important note:"
" this will NOT work in a development environment."
),
"ACTIVATE_HTTPS",
config,
defaults,
)
if run_for_prod:
ask_bool(
(
"Activate SSL/TLS certificates for HTTPS access? Important note:"
" this will NOT work in a development environment."
),
"ACTIVATE_HTTPS",
config,
defaults,
)
def ask(question, key, config, defaults):

View File

@ -1,10 +1,10 @@
# See docs: https://openedx.atlassian.net/wiki/spaces/LEARNER/pages/48792067/App+Configuration+Flags
API_HOST_URL: '{{ "https" if ACTIVATE_HTTPS else "http" }}://{{ LMS_HOST }}'
ENVIRONMENT_DISPLAY_NAME: 'tutor'
PLATFORM_NAME: '{{ PLATFORM_NAME }}'
PLATFORM_DESTINATION_NAME: '{{ LMS_HOST }}'
FEEDBACK_EMAIL_ADDRESS: 'support@{{ LMS_HOST }}'
OAUTH_CLIENT_ID: 'android'
API_HOST_URL: "{{ "https" if ACTIVATE_HTTPS else "http" }}://{{ LMS_HOST }}"
ENVIRONMENT_DISPLAY_NAME: "tutor"
PLATFORM_NAME: "{{ PLATFORM_NAME }}"
PLATFORM_DESTINATION_NAME: "{{ LMS_HOST }}"
FEEDBACK_EMAIL_ADDRESS: "{{ CONTACT_EMAIL }}"
OAUTH_CLIENT_ID: "android"
COURSE_VIDEOS_ENABLED: true
CERTIFICATES_ENABLED: true

View File

@ -13,7 +13,7 @@ server {
server {
{% if ACTIVATE_HTTPS %}listen 443 {{ "" if WEB_PROXY else "ssl" }};{% else %}listen 80;{% endif %}
server_name studio.localhost {{ CMS_HOST }};
server_name {{ CMS_HOST }};
{% if ACTIVATE_HTTPS and not WEB_PROXY %}
ssl_certificate /etc/letsencrypt/live/{{ LMS_HOST }}/fullchain.pem;

View File

@ -14,7 +14,7 @@ server {
server {
{% if ACTIVATE_HTTPS %}listen 443 {{ "" if WEB_PROXY else "ssl" }};{% else %}listen 80;{% endif %}
server_name localhost preview.localhost {{ LMS_HOST }} preview.{{ LMS_HOST }};
server_name {{ LMS_HOST }} preview.{{ LMS_HOST }};
{% if ACTIVATE_HTTPS and not WEB_PROXY %}
ssl_certificate /etc/letsencrypt/live/{{ LMS_HOST }}/fullchain.pem;

View File

@ -1 +0,0 @@
{% include "apps/openedx/config/partials/auth.json" %}

View File

@ -10,8 +10,8 @@
{{ patch("cms-env-features", separator=",\n", suffix=",")|indent(4) }}
"CERTIFICATES_HTML_VIEW": true,
"PREVIEW_LMS_BASE": "preview.{{ LMS_HOST }}",
"DISABLE_STUDIO_SSO_OVER_LMS": {{ "false" if ACTIVATE_HTTPS else "true" }},
"ENABLE_COURSEWARE_INDEX": true,
"ENABLE_CSMH_EXTENDED": false,
"ENABLE_LIBRARY_INDEX": true
},
"LMS_ROOT_URL": "{{ "https" if ACTIVATE_HTTPS else "http" }}://{{ LMS_HOST }}",
@ -24,6 +24,7 @@
"CELERY_BROKER_USER": "{{ RABBITMQ_USERNAME }}",
"CELERY_BROKER_PASSWORD": "{{ RABBITMQ_PASSWORD }}",
"ALTERNATE_WORKER_QUEUES": "lms",
"ENABLE_COMPREHENSIVE_THEMING": true,
"COMPREHENSIVE_THEME_DIRS": ["/openedx/themes"],
"STATIC_ROOT_BASE": "/openedx/staticfiles",
"ELASTIC_SEARCH_CONFIG": [{
@ -37,7 +38,7 @@
"EMAIL_USE_TLS": {{ "true" if SMTP_USE_TLS else "false" }},
"HTTPS": "{{ "on" if ACTIVATE_HTTPS else "off" }}",
"LANGUAGE_CODE": "{{ LANGUAGE_CODE }}",
{% if ACTIVATE_HTTPS %}"SESSION_COOKIE_DOMAIN": ".{{ LMS_HOST|common_domain(CMS_HOST) }}",{% endif %}
"SESSION_COOKIE_DOMAIN": ".{{ LMS_HOST|common_domain(CMS_HOST) }}",
{{ patch("cms-env", separator=",\n", suffix=",")|indent(2) }}
"CACHES": {
"default": {
@ -86,5 +87,6 @@
"KEY_FUNCTION": "util.memcache.safe_key",
"LOCATION": "{{ MEMCACHED_HOST }}:{{ MEMCACHED_PORT }}"
}
}
},
{% include "apps/openedx/config/partials/auth.json" %}
}

View File

@ -1 +0,0 @@
{% include "apps/openedx/config/partials/auth.json" %}

View File

@ -12,11 +12,13 @@
"PREVIEW_LMS_BASE": "preview.{{ LMS_HOST }}",
"ENABLE_COURSE_DISCOVERY": true,
"ENABLE_COURSEWARE_SEARCH": true,
"ENABLE_CSMH_EXTENDED": false,
"ENABLE_DASHBOARD_SEARCH": true,
"ENABLE_COMBINED_LOGIN_REGISTRATION": true,
"ENABLE_GRADE_DOWNLOADS": true,
"ENABLE_MOBILE_REST_API": true,
"ENABLE_OAUTH2_PROVIDER": true
"ENABLE_OAUTH2_PROVIDER": true,
"ENABLE_THIRD_PARTY_AUTH": true
},
"LMS_ROOT_URL": "{{ "https" if ACTIVATE_HTTPS else "http" }}://{{ LMS_HOST }}",
"CMS_ROOT_URL": "{{ "https" if ACTIVATE_HTTPS else "http" }}://{{ CMS_HOST }}",
@ -29,6 +31,7 @@
"CELERY_BROKER_PASSWORD": "{{ RABBITMQ_PASSWORD }}",
"COMMENTS_SERVICE_URL": "http://{{ FORUM_HOST }}:4567",
"COMMENTS_SERVICE_KEY": "forumapikey",
"ENABLE_COMPREHENSIVE_THEMING": true,
"COMPREHENSIVE_THEME_DIRS": ["/openedx/themes"],
"STATIC_ROOT_BASE": "/openedx/staticfiles",
"ELASTIC_SEARCH_CONFIG": [{
@ -42,8 +45,7 @@
"EMAIL_USE_TLS": {{ "true" if SMTP_USE_TLS else "false" }},
"HTTPS": "{{ "on" if ACTIVATE_HTTPS else "off" }}",
"LANGUAGE_CODE": "{{ LANGUAGE_CODE }}",
"LOGIN_REDIRECT_WHITELIST": ["{{ CMS_HOST }}", "studio.localhost"],
{% if ACTIVATE_HTTPS %}"SESSION_COOKIE_DOMAIN": ".{{ LMS_HOST|common_domain(CMS_HOST) }}",{% endif %}
"SESSION_COOKIE_DOMAIN": ".{{ LMS_HOST|common_domain(CMS_HOST) }}",
{{ patch("lms-env", separator=",\n", suffix=",")|indent(2) }}
"CACHES": {
"default": {
@ -98,5 +100,6 @@
"KEY_FUNCTION": "util.memcache.safe_key",
"LOCATION": "{{ MEMCACHED_HOST }}:{{ MEMCACHED_PORT }}"
}
}
},
{% include "apps/openedx/config/partials/auth.json" %}
}

View File

@ -1,11 +1,10 @@
{
"SECRET_KEY": "{{ OPENEDX_SECRET_KEY }}",
"AWS_ACCESS_KEY_ID": "{{ OPENEDX_AWS_ACCESS_KEY }}",
"AWS_SECRET_ACCESS_KEY": "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}",
{{ patch("openedx-auth", separator=",\n", suffix=",")|indent(2) }}
"XQUEUE_INTERFACE": {
"django_auth": null,
"url": null
"django_auth": null,
"url": null
},
"CONTENTSTORE": {
"ENGINE": "xmodule.contentstore.mongo.MongoContentStore",
@ -25,6 +24,9 @@
{% if MONGODB_USERNAME and MONGODB_PASSWORD %}
"user": "{{ MONGODB_USERNAME }}",
"password": "{{ MONGODB_PASSWORD }}",
{% else %}
"user": null,
"password": null,
{% endif %}
"db": "{{ MONGODB_DATABASE }}"
},
@ -38,10 +40,9 @@
"PASSWORD": "{{ OPENEDX_MYSQL_PASSWORD }}",
"ATOMIC_REQUESTS": true,
"OPTIONS": {
"init_command": "SET sql_mode='STRICT_TRANS_TABLES'"
"init_command": "SET sql_mode='STRICT_TRANS_TABLES'"
}
}
},
"EMAIL_HOST_USER": "{{ SMTP_USERNAME }}",
"EMAIL_HOST_PASSWORD": "{{ SMTP_PASSWORD }}"
}
"EMAIL_HOST_PASSWORD": "{{ SMTP_PASSWORD }}"

View File

@ -2,10 +2,14 @@
import os
from cms.envs.devstack import *
LMS_BASE = "{{ LMS_HOST }}:8000"
LMS_ROOT_URL = "http://" + LMS_BASE
FEATURES["PREVIEW_LMS_BASE"] = "preview." + LMS_BASE
{% include "apps/openedx/settings/partials/common_cms.py" %}
# Setup correct webpack configuration file for development
WEBPACK_CONFIG_PATH = "webpack.dev.config.js"
{{ patch("openedx-development-settings") }}
{{ patch("openedx-cms-development-settings") }}
{{ patch("openedx-cms-development-settings") }}

View File

@ -7,9 +7,6 @@ from cms.envs.production import *
ALLOWED_HOSTS = [
ENV_TOKENS.get("CMS_BASE"),
"cms",
"127.0.0.1",
"localhost",
"studio.localhost",
]
{{ patch("openedx-cms-production-settings") }}

View File

@ -7,5 +7,18 @@ from lms.envs.devstack import *
# Setup correct webpack configuration file for development
WEBPACK_CONFIG_PATH = "webpack.dev.config.js"
SESSION_COOKIE_DOMAIN = ".{{ LMS_HOST|common_domain(CMS_HOST) }}"
CMS_BASE = "{{ CMS_HOST}}:8001"
CMS_ROOT_URL = "http://{}".format(CMS_BASE)
LOGIN_REDIRECT_WHITELIST.append(CMS_BASE)
FEATURES['ENABLE_COURSEWARE_MICROFRONTEND'] = False
LOGGING["loggers"]["oauth2_provider"] = {
"handlers": ["console"],
"level": "DEBUG"
}
{{ patch("openedx-development-settings") }}
{{ patch("openedx-lms-development-settings") }}

View File

@ -8,9 +8,6 @@ ALLOWED_HOSTS = [
ENV_TOKENS.get("LMS_BASE"),
FEATURES["PREVIEW_LMS_BASE"],
"lms",
"127.0.0.1",
"localhost",
"preview.localhost",
]
# Required to display all courses on start page

View File

@ -1,4 +1,6 @@
####### Settings common to LMS and CMS
import json
import os
DEFAULT_FROM_EMAIL = ENV_TOKENS["CONTACT_EMAIL"]
DEFAULT_FEEDBACK_EMAIL = ENV_TOKENS["CONTACT_EMAIL"]
@ -15,6 +17,9 @@ API_ACCESS_FROM_EMAIL = ENV_TOKENS["CONTACT_EMAIL"]
# Load module store settings from config files
update_module_store_settings(MODULESTORE, doc_store_settings=DOC_STORE_CONFIG)
DATA_DIR = "/openedx/data/"
for store in MODULESTORE["default"]["OPTIONS"]["stores"]:
store["OPTIONS"]["fs_root"] = DATA_DIR
# Set uploaded media file path
MEDIA_ROOT = "/openedx/media/"
@ -23,6 +28,10 @@ MEDIA_ROOT = "/openedx/media/"
VIDEO_IMAGE_SETTINGS["STORAGE_KWARGS"]["location"] = MEDIA_ROOT
VIDEO_TRANSCRIPTS_SETTINGS["STORAGE_KWARGS"]["location"] = MEDIA_ROOT
ORA2_FILEUPLOAD_BACKEND = "filesystem"
ORA2_FILEUPLOAD_ROOT = "/openedx/data/ora2"
ORA2_FILEUPLOAD_CACHE_NAME = "ora2-storage"
# Change syslog-based loggers which don't work inside docker containers
LOGGING["handlers"]["local"] = {
"class": "logging.handlers.WatchedFileHandler",
@ -36,6 +45,15 @@ LOGGING["handlers"]["tracking"] = {
"formatter": "standard",
}
LOGGING["loggers"]["tracking"]["handlers"] = ["console", "local", "tracking"]
# Disable django/drf deprecation warnings
import logging
import warnings
from django.utils.deprecation import RemovedInDjango30Warning, RemovedInDjango31Warning
from rest_framework import RemovedInDRF310Warning, RemovedInDRF311Warning
warnings.simplefilter('ignore', RemovedInDjango30Warning)
warnings.simplefilter('ignore', RemovedInDjango31Warning)
warnings.simplefilter('ignore', RemovedInDRF310Warning)
warnings.simplefilter('ignore', RemovedInDRF311Warning)
# Email
EMAIL_USE_SSL = {{ SMTP_USE_SSL }}
@ -51,5 +69,40 @@ LOCALE_PATHS.append("/openedx/locale/user/locale")
# Allow the platform to include itself in an iframe
X_FRAME_OPTIONS = "SAMEORIGIN"
{% set jwt_rsa_key = rsa_import_key(JWT_RSA_PRIVATE_KEY) %}
JWT_AUTH["JWT_ISSUER"] = "{{ JWT_COMMON_ISSUER }}"
JWT_AUTH["JWT_AUDIENCE"] = "{{ JWT_COMMON_AUDIENCE }}"
JWT_AUTH["JWT_SECRET_KEY"] = "{{ JWT_COMMON_SECRET_KEY }}"
JWT_AUTH["JWT_PRIVATE_SIGNING_JWK"] = json.dumps(
{
"kid": "openedx",
"kty": "RSA",
"e": "{{ jwt_rsa_key.e|long_to_base64 }}",
"d": "{{ jwt_rsa_key.d|long_to_base64 }}",
"n": "{{ jwt_rsa_key.n|long_to_base64 }}",
"p": "{{ jwt_rsa_key.p|long_to_base64 }}",
"q": "{{ jwt_rsa_key.q|long_to_base64 }}",
}
)
JWT_AUTH["JWT_PUBLIC_SIGNING_JWK_SET"] = json.dumps(
{
"keys": [
{
"kid": "openedx",
"kty": "RSA",
"e": "{{ jwt_rsa_key.e|long_to_base64 }}",
"n": "{{ jwt_rsa_key.n|long_to_base64 }}",
}
]
}
)
JWT_AUTH["JWT_ISSUERS"] = [
{
"ISSUER": "{{ JWT_COMMON_ISSUER }}",
"AUDIENCE": "{{ JWT_COMMON_AUDIENCE }}",
"SECRET_KEY": "{{ OPENEDX_SECRET_KEY }}"
}
]
{{ patch("openedx-common-settings") }}
######## End of settings common to LMS and CMS
######## End of settings common to LMS and CMS

View File

@ -2,10 +2,13 @@
######## Common CMS settings
STUDIO_NAME = u"{{ PLATFORM_NAME }} - Studio"
MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB = 100
FRONTEND_LOGIN_URL = LMS_ROOT_URL + '/login'
FRONTEND_LOGOUT_URL = LMS_ROOT_URL + '/logout'
FRONTEND_REGISTER_URL = LMS_ROOT_URL + '/register'
# Create folders if necessary
for folder in [LOG_DIR, MEDIA_ROOT, STATIC_ROOT_BASE]:
if not os.path.exists(folder):

View File

@ -2,17 +2,14 @@
######## Common LMS settings
LOGIN_REDIRECT_WHITELIST = ["{{ CMS_HOST }}"]
LEARNING_MICROFRONTEND_URL = None
# Fix media files paths
VIDEO_IMAGE_SETTINGS["STORAGE_KWARGS"]["location"] = MEDIA_ROOT
VIDEO_TRANSCRIPTS_SETTINGS["STORAGE_KWARGS"]["location"] = MEDIA_ROOT
PROFILE_IMAGE_BACKEND["options"]["location"] = os.path.join(
MEDIA_ROOT, "profile-images/"
)
ORA2_FILEUPLOAD_BACKEND = "filesystem"
ORA2_FILEUPLOAD_ROOT = "/openedx/data/ora2"
ORA2_FILEUPLOAD_CACHE_NAME = "ora2-storage"
GRADES_DOWNLOAD = {
"STORAGE_TYPE": "",
"STORAGE_KWARGS": {
@ -24,17 +21,11 @@ GRADES_DOWNLOAD = {
COURSE_CATALOG_VISIBILITY_PERMISSION = "see_in_catalog"
COURSE_ABOUT_VISIBILITY_PERMISSION = "see_about_page"
# JWT is authentication for other openedx services
JWT_AUTH["JWT_ISSUER"] = "{{ JWT_COMMON_ISSUER }}"
JWT_AUTH["JWT_AUDIENCE"] = "{{ JWT_COMMON_AUDIENCE }}"
JWT_AUTH["JWT_SECRET_KEY"] = "{{ JWT_COMMON_SECRET_KEY }}"
JWT_AUTH["JWT_PRIVATE_SIGNING_JWK"] = None
# Allow insecure oauth2 for local interaction with local containers
OAUTH_ENFORCE_SECURE = False
# Create folders if necessary
for folder in [LOG_DIR, MEDIA_ROOT, STATIC_ROOT_BASE, ORA2_FILEUPLOAD_ROOT]:
for folder in [DATA_DIR, LOG_DIR, MEDIA_ROOT, STATIC_ROOT_BASE, ORA2_FILEUPLOAD_ROOT]:
if not os.path.exists(folder):
os.makedirs(folder)

View File

@ -15,22 +15,22 @@ RUN wget -O /tmp/dockerize.tar.gz https://github.com/jwilder/dockerize/releases/
# Install ruby-build for building specific version of ruby
# The ruby-build version should be periodically updated to reflect the latest release
ARG RUBY_BUILD_VERSION=v20200224
ARG RUBY_BUILD_VERSION=v20200401
RUN git clone https://github.com/rbenv/ruby-build.git --branch $RUBY_BUILD_VERSION /openedx/ruby-build
WORKDIR /openedx/ruby-build
RUN PREFIX=/usr/local ./install.sh
# Install ruby and some specific dependencies
ARG RUBY_VERSION=2.4.1
ARG BUNDLER_VERSION=1.11.2
ARG RAKE_VERSION=10.4.2
ARG RUBY_VERSION=2.5.7
ARG BUNDLER_VERSION=1.17.3
ARG RAKE_VERSION=13.0.1
RUN ruby-build $RUBY_VERSION /openedx/ruby
ENV PATH "/openedx/ruby/bin:$PATH"
RUN gem install bundler -v $BUNDLER_VERSION
RUN gem install rake -v $RAKE_VERSION
# Install forum
RUN git clone https://github.com/edx/cs_comments_service.git --branch open-release/ironwood.2 --depth 1 /openedx/cs_comments_service
RUN git clone https://github.com/edx/cs_comments_service.git --branch open-release/juniper.1 --depth 1 /openedx/cs_comments_service
WORKDIR /openedx/cs_comments_service
RUN bundle install --deployment
@ -44,6 +44,7 @@ ENV NEW_RELIC_ENABLE false
ENV API_KEY forumapikey
ENV SEARCH_SERVER "http://elasticsearch:9200"
ENV MONGODB_AUTH ""
ENV MONGOID_AUTH_MECH ""
ENV MONGODB_HOST "mongodb"
ENV MONGODB_PORT "27017"
EXPOSE 4567

View File

@ -8,7 +8,8 @@ RUN apt update && \
# Install dev python requirements
RUN pip install -r requirements/edx/development.txt
RUN pip install ipdb==0.12.2 ipython==5.8.0
# Pinning the jedi library is required in python 3.5 to avoid deprecation warnings from ipython
RUN pip install ipdb==0.13.2 ipython==7.9.0 jedi==0.15.2
# Recompile static assets: in development mode all static assets are stored in edx-platform,
# and the location of these files is stored in webpack-stats.json. If we don't recompile

View File

@ -6,10 +6,19 @@ MAINTAINER Overhang.io <contact@overhang.io>
# Install system requirements
RUN apt update && \
# Global requirements
apt install -y language-pack-en git python-virtualenv build-essential software-properties-common curl git-core libxml2-dev libxslt1-dev python-virtualenv libmysqlclient-dev python-apt python-dev libxmlsec1-dev libfreetype6-dev swig gcc g++ \
apt install -y language-pack-en git build-essential software-properties-common curl git-core libmysqlclient-dev libxml2-dev libxslt1-dev libxmlsec1-dev libfreetype6-dev swig gcc g++ \
# pyenv/python requirements
libbz2-dev libreadline-dev \
# openedx requirements
gettext gfortran graphviz graphviz-dev libffi-dev libfreetype6-dev libgeos-dev libjpeg8-dev liblapack-dev libpng12-dev libsqlite3-dev libxml2-dev libxmlsec1-dev libxslt1-dev lynx nodejs npm ntp pkg-config \
&& rm -rf /var/lib/apt/lists/*
ENV LC_ALL en_US.UTF-8
# Install python with pyenv
ARG PYTHON_VERSION=3.5.9
ENV PYENV_ROOT /opt/pyenv
RUN git clone https://github.com/pyenv/pyenv $PYENV_ROOT --branch v1.2.18 --depth 1 \
&& $PYENV_ROOT/bin/pyenv install $PYTHON_VERSION
# Dockerize will be useful to wait for mysql DB availability
ARG DOCKERIZE_VERSION=v0.6.1
@ -17,59 +26,51 @@ RUN curl -L -o /tmp/dockerize.tar.gz https://github.com/jwilder/dockerize/releas
&& tar -C /usr/local/bin -xzvf /tmp/dockerize.tar.gz \
&& rm /tmp/dockerize.tar.gz
# Checkout edx-platform code: https://github.com/edx/edx-platform/commits/open-release/ironwood.master
# Because we are pulling from a branch, and not a tag, you should manually modify the
# date hash below to force clearing the docker cache.
# Checkout edx-platform code
ARG EDX_PLATFORM_REPOSITORY=https://github.com/edx/edx-platform.git
ARG EDX_PLATFORM_VERSION=open-release/ironwood.master
ARG EDX_PLATFORM_VERSION_DATE=20200505
ARG EDX_PLATFORM_VERSION=open-release/juniper.1
RUN mkdir -p /openedx/edx-platform && \
echo "Pulling $EDX_PLATFORM_VERSION tag from $EDX_PLATFORM_REPOSITORY ($EDX_PLATFORM_VERSION_DATE)" && \
git clone $EDX_PLATFORM_REPOSITORY --branch $EDX_PLATFORM_VERSION --depth 1 /openedx/edx-platform
WORKDIR /openedx/edx-platform
# Apply patches
# Allow SigV4 authentication for video uploads to S3 https://github.com/edx/edx-platform/pull/22080
RUN curl https://github.com/overhangio/edx-platform/commit/0d4f6cc3433013960b28e963c4094ef2a2a92f04.patch | git apply -
# Resolve missing tasks in CMS
# https://github.com/edx/edx-platform/pull/21297/
# https://github.com/edx/edx-platform/pull/21305/
RUN curl https://github.com/edx/edx-platform/commit/adb2c672e4d17cc1c42bdc206a0051e0fa16b5be.patch | git apply -
RUN curl https://github.com/edx/edx-platform/commit/b7ecd80a2bef0d845c3bce97818e70fb3ed9e36d.patch | git apply -
# Patch edx-platform
# Get rid of lepl-related warnings
# https://github.com/edx/edx-platform/pull/24059
# https://github.com/overhangio/edx-platform/tree/overhangio/lepl-rfc6266-warning
RUN curl https://github.com/overhangio/edx-platform/commit/5f21bbe77056d71ca61b97b6badcff3c1a31b858.patch | git apply -
# Fix creation of LTI provider objects
# https://github.com/edx/edx-platform/pull/24055
# https://github.com/overhangio/edx-platform/tree/regisb/fix-lti-provider-admin
RUN curl https://github.com/overhangio/edx-platform/commit/089b26eed0302ed1f9a5b24c5f3e563dd44abb04.patch | git apply -
# Download extra locales to /openedx/locale/contrib/locale
RUN cd /tmp \
&& curl -L -o openedx-i18n.tar.gz https://github.com/openedx/openedx-i18n/archive/ironwood.tar.gz \
&& curl -L -o openedx-i18n.tar.gz https://github.com/openedx/openedx-i18n/archive/juniper.1.tar.gz \
&& tar xzf /tmp/openedx-i18n.tar.gz \
&& mkdir -p /openedx/locale/contrib \
&& mv openedx-i18n-ironwood/edx-platform/locale /openedx/locale/contrib \
&& mv openedx-i18n-juniper.1/edx-platform/locale /openedx/locale/contrib \
&& rm -rf openedx-i18n*
# Install python requirements in a virtualenv
RUN virtualenv /openedx/venv
RUN $PYENV_ROOT/versions/$PYTHON_VERSION/bin/pyvenv /openedx/venv
ENV PATH /openedx/venv/bin:${PATH}
ENV VIRTUAL_ENV /openedx/venv/
RUN pip install setuptools==39.0.1 pip==9.0.3
RUN pip install setuptools==44.1.0 pip==20.0.2 wheel==0.34.2
RUN pip install -r requirements/edx/base.txt
# Install patched version of ora2
RUN pip uninstall -y ora2 && \
pip install git+https://github.com/overhangio/edx-ora2.git@2.2.0-patched#egg=ora2==2.2.0
RUN pip install https://github.com/overhangio/edx-ora2/archive/overhangio/boto2to3.zip
# Install patched version of edx-oauth2-provider
RUN pip install git+https://github.com/overhangio/edx-oauth2-provider.git@1.2.3#egg=edx-oauth2-provider==1.2.3
# Install ironwood-compatible scorm xblock
RUN pip install "openedx-scorm-xblock<10.0.0,>=9.2.0"
# Install juniper-compatible scorm xblock
RUN pip install "openedx-scorm-xblock<11.0.0,>=10.0.0"
# Install a recent version of nodejs
RUN nodeenv /openedx/nodeenv --node=8.9.3 --prebuilt
RUN nodeenv /openedx/nodeenv --node=12.13.0 --prebuilt
ENV PATH /openedx/nodeenv/bin:${PATH}
# Install nodejs requirements
ARG NPM_REGISTRY=https://registry.npmjs.org/
RUN npm set progress=false \
&& npm install --verbose --registry=$NPM_REGISTRY
RUN npm install --verbose --registry=$NPM_REGISTRY
ENV PATH ./node_modules/.bin:${PATH}
# Install private requirements: this is useful for installing custom xblocks.
@ -78,10 +79,13 @@ RUN cd /openedx/requirements/ \
&& touch ./private.txt \
&& pip install -r ./private.txt
# Create folder that will store *.env.json and *.auth.json files, as well as
# Create folder that will store lms/cms.env.json files, as well as
# the tutor-specific settings files.
RUN mkdir -p /openedx/config ./lms/envs/tutor ./cms/envs/tutor
ENV CONFIG_ROOT /openedx/config
COPY revisions.yml /openedx/config/
ENV LMS_CFG /openedx/config/lms.env.json
ENV STUDIO_CFG /openedx/config/cms.env.json
ENV REVISION_CFG /openedx/config/revisions.yml
COPY settings/lms/*.py ./lms/envs/tutor/
COPY settings/cms/*.py ./cms/envs/tutor/
@ -111,6 +115,10 @@ ENV PATH /openedx/bin:${PATH}
# and run each one individually to collect the production static assets to
# /openedx/staticfiles.
ENV NO_PYTHON_UNINSTALL 1
ENV NO_PREREQ_INSTALL 1
# We need to rely on a separate openedx-assets command to accelerate asset processing.
# For instance, we don't want to run all steps of asset collection every time the theme
# is modified.
RUN openedx-assets xmodule \
&& openedx-assets npm \
&& openedx-assets webpack --env=prod \

View File

@ -9,7 +9,6 @@ import traceback
from path import Path
from pavelib import assets
from xmodule import static_content as xmodule_static_content
DEFAULT_STATIC_ROOT = "/openedx/staticfiles"
@ -99,8 +98,18 @@ def run_build(args):
def run_xmodule(_args):
# Collecting xmodule assets is incompatible with setting the django path, because
# of an unfortunate call to settings.configure()
django_settings_module = os.environ.get("DJANGO_SETTINGS_MODULE")
if django_settings_module:
os.environ.pop("DJANGO_SETTINGS_MODULE")
sys.argv[1:] = ["common/static/xmodule"]
xmodule_static_content.main()
import xmodule.static_content
xmodule.static_content.main()
if django_settings_module:
os.environ["DJANGO_SETTINGS_MODULE"] = django_settings_module
def run_npm(_args):

View File

@ -0,0 +1 @@
EDX_PLATFORM_REVISION: juniper

View File

@ -4,7 +4,9 @@ Bare minimum settings for collecting production assets.
from ..common import *
from openedx.core.lib.derived import derive_settings
ENABLE_COMPREHENSIVE_THEMING = True
COMPREHENSIVE_THEME_DIRS.append('/openedx/themes')
STATIC_ROOT_BASE = '/openedx/staticfiles'
SECRET_KEY = 'secret'

View File

@ -33,7 +33,7 @@ DOCKER_IMAGE_OPENEDX_DEV: "overhangio/openedx-dev:{{ TUTOR_VERSION }}"
DOCKER_IMAGE_ANDROID: "overhangio/openedx-android:{{ TUTOR_VERSION }}"
DOCKER_IMAGE_FORUM: "overhangio/openedx-forum:{{ TUTOR_VERSION }}"
DOCKER_IMAGE_MEMCACHED: "memcached:1.4.38"
DOCKER_IMAGE_MONGODB: "mongo:3.2.16"
DOCKER_IMAGE_MONGODB: "mongo:3.6.18"
DOCKER_IMAGE_MYSQL: "mysql:5.6.36"
DOCKER_IMAGE_ELASTICSEARCH: "elasticsearch:1.5.2"
DOCKER_IMAGE_NGINX: "nginx:1.13"
@ -49,6 +49,7 @@ FORUM_HOST: "forum"
JWT_COMMON_AUDIENCE: "openedx"
JWT_COMMON_ISSUER: "{% if ACTIVATE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/oauth2"
JWT_COMMON_SECRET_KEY: "{{ OPENEDX_SECRET_KEY }}"
JWT_RSA_PRIVATE_KEY: "{{ 2048|rsa_private_key }}"
K8S_NAMESPACE: "openedx"
LANGUAGE_CODE: "en"
MEMCACHED_HOST: "memcached"
@ -61,6 +62,7 @@ MONGODB_PASSWORD: ""
OPENEDX_CMS_GUNICORN_WORKERS: 2
OPENEDX_LMS_GUNICORN_WORKERS: 2
OPENEDX_MYSQL_DATABASE: "openedx"
OPENEDX_CSMH_MYSQL_DATABASE: "{{ OPENEDX_MYSQL_DATABASE }}_csmh"
OPENEDX_MYSQL_USERNAME: "openedx"
MYSQL_HOST: "mysql"
MYSQL_PORT: 3306

View File

@ -6,6 +6,10 @@ x-openedx-service:
environment:
SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.development}
volumes:
# Settings & config
- ../apps/openedx/settings/lms/:/openedx/edx-platform/lms/envs/tutor/
- ../apps/openedx/settings/cms/:/openedx/edx-platform/cms/envs/tutor/
- ../apps/openedx/config/:/openedx/config/
# theme files
- ../build/openedx/themes:/openedx/themes
# editable requirements
@ -17,13 +21,20 @@ services:
command: ./manage.py lms runserver 0.0.0.0:8000
ports:
- "8000:8000"
networks:
default:
aliases:
- "{{ LMS_HOST }}"
cms:
<<: *openedx-service
command: ./manage.py cms runserver 0.0.0.0:8000
ports:
- "8001:8000"
lms-worker:
<<: *openedx-service
cms-worker:
<<: *openedx-service
@ -32,5 +43,5 @@ services:
<<: *openedx-service
command: openedx-assets watch-themes --env dev
restart: unless-stopped
{{ patch("local-docker-compose-dev-services")|indent(2) }}

View File

@ -2,8 +2,8 @@ export DJANGO_SETTINGS_MODULE=$SERVICE_VARIANT.envs.$SETTINGS
echo "Loading settings $DJANGO_SETTINGS_MODULE"
# Import demo course
git clone https://github.com/edx/edx-demo-course --branch open-release/ironwood.2 --depth 1 ../edx-demo-course
git clone https://github.com/edx/edx-demo-course --branch open-release/juniper.1 --depth 1 ../edx-demo-course
python ./manage.py cms import ../data ../edx-demo-course
# Re-index courses
./manage.py cms reindex_course --all --setup
./manage.py cms reindex_course --all --setup

View File

@ -5,11 +5,14 @@ echo "Loading settings $DJANGO_SETTINGS_MODULE"
./manage.py lms migrate
./manage.py lms create_oauth2_client \
"http://androidapp.com" "http://androidapp.com/redirect" public \
--client_id android --client_secret {{ ANDROID_OAUTH2_SECRET }} \
--trusted
# Create oauth credentials for Android application
./manage.py lms create_dot_application \
--client-id android \
--client-secret {{ ANDROID_OAUTH2_SECRET }} \
--grant-type password \
android \
login_service_user
# Fix incorrect uploaded file path
if [ -d /openedx/data/uploads/ ]; then
if [ -n "$(ls -A /openedx/data/uploads/)" ]; then

View File

@ -14,5 +14,6 @@ do
done
echo "MySQL is up and running"
# edx-platform database
mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'CREATE DATABASE IF NOT EXISTS {{ OPENEDX_MYSQL_DATABASE }};'
mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'GRANT ALL ON {{ OPENEDX_MYSQL_DATABASE }}.* TO "{{ OPENEDX_MYSQL_USERNAME }}"@"%" IDENTIFIED BY "{{ OPENEDX_MYSQL_PASSWORD }}";'
mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'GRANT ALL ON {{ OPENEDX_MYSQL_DATABASE }}.* TO "{{ OPENEDX_MYSQL_USERNAME }}"@"%" IDENTIFIED BY "{{ OPENEDX_MYSQL_PASSWORD }}";'

View File

@ -0,0 +1,30 @@
version: "3.7"
services:
openedx-assets:
image: {{ DOCKER_REGISTRY }}{{ DOCKER_IMAGE_OPENEDX }}
volumes:
- ../../data/openedx:/var/www/openedx
command: sh -c "rm -rf /var/www/openedx/staticfiles && cp -r /openedx/staticfiles/ /var/www/openedx/"
nginx:
image: {{ DOCKER_REGISTRY }}{{ DOCKER_IMAGE_NGINX }}
restart: unless-stopped
ports:
- "{{ NGINX_HTTP_PORT }}:80"
- "{{ NGINX_HTTPS_PORT }}:443"
{% if not WEB_PROXY %}
networks:
default:
aliases:
- "{{ LMS_HOST }}"
{{ patch("local-docker-compose-nginx-aliases")|indent(10) }}
{% endif %}
volumes:
- ../apps/nginx:/etc/nginx/conf.d/:ro
- ../../data/openedx:/var/www/openedx:ro
- ../../data/openedx-media:/var/www/openedx-media:ro
{% if ACTIVATE_HTTPS %}- ../../data/letsencrypt:/etc/letsencrypt/:ro{% endif %}
{{ patch("local-docker-compose-nginx-volumes")|indent(6) }}
depends_on: {{ [("lms", ACTIVATE_LMS), ("cms", ACTIVATE_CMS)]|list_if }}
{{ patch("local-docker-compose-prod-services")|indent(2) }}

View File

@ -43,29 +43,6 @@ services:
- ../../data/elasticsearch:/usr/share/elasticsearch/data
{% endif %}
openedx-assets:
image: {{ DOCKER_REGISTRY }}{{ DOCKER_IMAGE_OPENEDX }}
volumes:
- ../../data/openedx:/var/www/openedx
command: sh -c "rm -rf /var/www/openedx/staticfiles && cp -r /openedx/staticfiles/ /var/www/openedx/"
nginx:
image: {{ DOCKER_REGISTRY }}{{ DOCKER_IMAGE_NGINX }}
restart: unless-stopped
ports:
- "{{ NGINX_HTTP_PORT }}:80"
- "{{ NGINX_HTTPS_PORT }}:443"
networks:
default:
aliases: [{{ patch("local-docker-compose-nginx-aliases", separator=", ") }}]
volumes:
- ../apps/nginx:/etc/nginx/conf.d/:ro
- ../../data/openedx:/var/www/openedx:ro
- ../../data/openedx-media:/var/www/openedx-media:ro
{% if ACTIVATE_HTTPS %}- ../../data/letsencrypt:/etc/letsencrypt/:ro{% endif %}
{{ patch("local-docker-compose-nginx-volumes")|indent(6) }}
depends_on: {{ [("lms", ACTIVATE_LMS), ("cms", ACTIVATE_CMS)]|list_if }}
{% if ACTIVATE_RABBITMQ %}
rabbitmq:
image: {{ DOCKER_REGISTRY }}{{ DOCKER_IMAGE_RABBITMQ }}
@ -142,6 +119,7 @@ services:
{% if ACTIVATE_MONGODB %}- mongodb{% endif %}
{% if ACTIVATE_RABBITMQ %}- rabbitmq{% endif %}
{% if ACTIVATE_SMTP %}- smtp{% endif %}
{% if ACTIVATE_LMS %}- lms{% endif %}
{{ patch("local-docker-compose-cms-dependencies")|indent(6) }}
{% endif %}

View File

@ -1,3 +1,4 @@
import base64
from crypt import crypt
from hmac import compare_digest
import json
@ -5,10 +6,12 @@ import os
import random
import shutil
import string
import struct
import subprocess
import sys
import click
from Crypto.PublicKey import RSA
from . import exceptions
from . import fmt
@ -77,6 +80,41 @@ def reverse_host(domain):
return ".".join(domain.split(".")[::-1])
def rsa_private_key(bits=2048):
"""
Export an RSA private key in PEM format.
"""
key = RSA.generate(bits)
return key.export_key().decode()
def rsa_import_key(key):
"""
Import PEM-formatted RSA key and return the corresponding object.
"""
return RSA.import_key(key.encode())
def long_to_base64(n):
"""
Borrowed from jwkest.__init__
"""
def long2intarr(long_int):
_bytes = []
while long_int:
long_int, r = divmod(long_int, 256)
_bytes.insert(0, r)
return _bytes
bys = long2intarr(n)
data = struct.pack("%sB" % len(bys), *bys)
if not data:
data = "\x00"
s = base64.urlsafe_b64encode(data).rstrip(b"=")
return s.decode("ascii")
def walk_files(path):
"""
Iterate on file paths located in directory.