tutor/tutor/config.py

264 lines
7.6 KiB
Python
Raw Normal View History

import os
from . import env, exceptions, fmt, plugins, serialize, utils
from .types import Config, cast_config
CONFIG_FILENAME = "config.yml"
def load(root: str) -> Config:
"""
Load full configuration.
This will raise an exception if there is no current configuration in the
project root. A warning will also be printed if the version from disk
differs from the package version.
"""
if not os.path.exists(config_path(root)):
raise exceptions.TutorError(
"Project root does not exist. Make sure to generate the initial "
"configuration with `tutor config save --interactive` or `tutor local "
"quickstart` prior to running other commands."
)
env.check_is_up_to_date(root)
return load_full(root)
def load_minimal(root: str) -> Config:
"""
Load a minimal configuration composed of the user and the base config.
This configuration is not suitable for rendering templates, as it is incomplete.
"""
config = get_user(root)
update_with_base(config)
render_full(config)
return config
def load_full(root: str) -> Config:
"""
Load a full configuration, with user, base and defaults.
Return:
current (dict): params currently saved in config.yml
defaults (dict): default values of params which might be missing from the
current config
"""
config = get_user(root)
update_with_base(config)
update_with_defaults(config)
render_full(config)
return config
def update_with_base(config: Config) -> None:
"""
Add base configuration to the config object.
Note that configuration entries are unrendered at this point.
"""
base = get_base(config)
merge(config, base)
def update_with_defaults(config: Config) -> None:
"""
Add default configuration to the config object.
Note that configuration entries are unrendered at this point.
"""
defaults = get_defaults(config)
merge(config, defaults)
def update_with_env(config: Config) -> None:
"""
Override config values from environment variables.
"""
overrides = {}
for k in config.keys():
env_var = "TUTOR_" + k
if env_var in os.environ:
overrides[k] = serialize.parse(os.environ[env_var])
config.update(overrides)
def get_user(root: str) -> Config:
"""
Get the user configuration from the tutor root.
Overrides from environment variables are loaded as well.
"""
feat: upgrade to Maple - A shared cookie domain between lms and cms is no longer recommended: https://github.com/edx/edx-platform/blob/master/docs/guides/studio_oauth.rst - refactor: clean mounted data folder in lms/cms. In Lilac, the bind-mounted lms/data and cms/data folders are a mess because new folders are created there for every new course organisation. These folders are empty. As far as we know they are useless... With this change we move these folders to a dedicated "modulestore" subdirectory; which corresponds better to the initial intent of the fs_root setting. - fix: frontend failure during login to the lms. See: https://github.com/openedx/build-test-release-wg/issues/104 - feat: move all forum-related code to a dedicated plugin. Forum is an optional feature, and as such it deserves its own plugin. Starting from Maple, users will be able to install the forum from https://github.com/overhangio/tutor-forum/ - migrate from DCS_* session cookie settings to SESSION_*. That's because edx-platform no longer depends on django-cookies-samesite. Close https://github.com/openedx/build-test-release-wg/issues/110 - get rid of tons of deprecation warnings in the lms/cms - feat: make it possible to point to themed assets. Cherry-picking this change makes it possible to point to themed assets with a theme-agnostic url, notably from MFEs. - Install all official plugins as part of the `tutor[full]` package. - Don't print error messages about loading plugins during autocompletion. - Prompt for image building when upgrading from one release to the next. - Add `tutor local start --skip-build` option to skip building Docker images. Close #450. Close #545.
2021-10-18 09:43:40 +00:00
convert_json2yml(root)
path = config_path(root)
config = {}
if os.path.exists(path):
config = get_yaml_file(path)
upgrade_obsolete(config)
update_with_env(config)
return config
2019-06-05 17:57:30 +00:00
def get_base(config: Config) -> Config:
"""
Load the base configuration.
Entries in this configuration are unrendered.
"""
base = get_template("base.yml")
# Load base values from plugins
for plugin in plugins.iter_enabled(config):
# Add new config key/values
for key, value in plugin.config_add.items():
new_key = plugin.config_key(key)
base[new_key] = value
# Set existing config key/values
for key, value in plugin.config_set.items():
base[key] = value
return base
def get_defaults(config: Config) -> Config:
"""
Get default configuration, including from plugins.
Entries in this configuration are unrendered.
"""
defaults = get_template("defaults.yml")
for plugin in plugins.iter_enabled(config):
# Create new defaults
for key, value in plugin.config_defaults.items():
defaults[plugin.config_key(key)] = value
update_with_env(defaults)
return defaults
def get_template(filename: str) -> Config:
"""
Get one of the configuration templates.
Entries in this configuration are unrendered.
"""
config = serialize.load(env.read_template_file("config", filename))
return cast_config(config)
def get_yaml_file(path: str) -> Config:
"""
Load config from yaml file.
"""
with open(path) as f:
config = serialize.load(f.read())
return cast_config(config)
def merge(config: Config, base: Config) -> None:
"""
Merge base values with user configuration. Values are only added if not
already present.
Note that this function does not perform the rendering step of the
configuration entries.
"""
for key, value in base.items():
if key not in config:
config[key] = value
def render_full(config: Config) -> None:
"""
Fill and render an existing configuration with defaults.
It is generally necessary to apply this function before rendering templates,
otherwise configuration entries may not be rendered.
"""
for key, value in config.items():
config[key] = env.render_unknown(config, value)
def is_service_activated(config: Config, service: str) -> bool:
return config["RUN_" + service.upper()] is not False
def upgrade_obsolete(config: Config) -> None:
# Openedx-specific mysql passwords
if "MYSQL_PASSWORD" in config:
config["MYSQL_ROOT_PASSWORD"] = config["MYSQL_PASSWORD"]
config["OPENEDX_MYSQL_PASSWORD"] = config["MYSQL_PASSWORD"]
config.pop("MYSQL_PASSWORD")
if "MYSQL_DATABASE" in config:
config["OPENEDX_MYSQL_DATABASE"] = config.pop("MYSQL_DATABASE")
if "MYSQL_USERNAME" in config:
config["OPENEDX_MYSQL_USERNAME"] = config.pop("MYSQL_USERNAME")
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
if "RUN_NOTES" in config:
if config["RUN_NOTES"]:
2019-07-03 14:09:33 +00:00
plugins.enable(config, "notes")
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
config.pop("RUN_NOTES")
if "RUN_XQUEUE" in config:
if config["RUN_XQUEUE"]:
plugins.enable(config, "xqueue")
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
config.pop("RUN_XQUEUE")
if "SECRET_KEY" in config:
config["OPENEDX_SECRET_KEY"] = config.pop("SECRET_KEY")
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
# Replace WEB_PROXY by RUN_CADDY
if "WEB_PROXY" in config:
config["RUN_CADDY"] = not config.pop("WEB_PROXY")
# Rename ACTIVATE_HTTPS to ENABLE_HTTPS
if "ACTIVATE_HTTPS" in config:
config["ENABLE_HTTPS"] = config.pop("ACTIVATE_HTTPS")
# Replace RUN_* variables by RUN_*
for name in [
"ACTIVATE_LMS",
"ACTIVATE_CMS",
"ACTIVATE_ELASTICSEARCH",
"ACTIVATE_MONGODB",
"ACTIVATE_MYSQL",
"ACTIVATE_REDIS",
"ACTIVATE_SMTP",
]:
if name in config:
config[name.replace("ACTIVATE_", "RUN_")] = config.pop(name)
# Replace RUN_CADDY by ENABLE_WEB_PROXY
if "RUN_CADDY" in config:
config["ENABLE_WEB_PROXY"] = config.pop("RUN_CADDY")
# Replace RUN_CADDY by ENABLE_WEB_PROXY
if "NGINX_HTTP_PORT" in config:
config["CADDY_HTTP_PORT"] = config.pop("NGINX_HTTP_PORT")
def convert_json2yml(root: str) -> None:
"""
Older versions of tutor used to have json config files.
"""
json_path = os.path.join(root, "config.json")
if not os.path.exists(json_path):
return
if os.path.exists(config_path(root)):
raise exceptions.TutorError(
f"Both config.json and {CONFIG_FILENAME} exist in {root}: only one of these files must exist to continue"
)
config = get_yaml_file(json_path)
save_config_file(root, config)
os.remove(json_path)
fmt.echo_info(
f"File config.json detected in {root} and converted to {CONFIG_FILENAME}"
)
def save_config_file(root: str, config: Config) -> None:
path = config_path(root)
utils.ensure_file_directory_exists(path)
with open(path, "w") as of:
serialize.dump(config, of)
fmt.echo_info(f"Configuration saved to {path}")
def config_path(root: str) -> str:
return os.path.join(root, CONFIG_FILENAME)