diff --git a/tests/test_config.py b/tests/test_config.py index 3fab30b..30f934f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,6 +2,8 @@ import unittest from unittest.mock import Mock, patch import tempfile +import click + from tutor import config as tutor_config from tutor import interactive from tutor.types import get_typed, Config @@ -9,7 +11,7 @@ from tutor.types import get_typed, Config class ConfigTests(unittest.TestCase): def test_version(self) -> None: - defaults = tutor_config.load_defaults() + defaults = tutor_config.get_defaults({}) self.assertNotIn("TUTOR_VERSION", defaults) def test_merge(self) -> None: @@ -18,22 +20,21 @@ class ConfigTests(unittest.TestCase): tutor_config.merge(config1, config2) self.assertEqual({"x": "y"}, config1) - def test_merge_render(self) -> None: + def test_merge_not_render(self) -> None: config: Config = {} - defaults = tutor_config.load_defaults() + base = tutor_config.get_base({}) with patch.object(tutor_config.utils, "random_string", return_value="abcd"): - tutor_config.merge(config, defaults) + tutor_config.merge(config, base) - self.assertEqual("abcd", config["MYSQL_ROOT_PASSWORD"]) + # Check that merge does not perform a rendering + self.assertNotEqual("abcd", config["MYSQL_ROOT_PASSWORD"]) @patch.object(tutor_config.fmt, "echo") - def test_update_twice(self, _: Mock) -> None: + def test_save_load(self, _: Mock) -> None: with tempfile.TemporaryDirectory() as root: - tutor_config.update(root) - config1 = tutor_config.load_user(root) - - tutor_config.update(root) - config2 = tutor_config.load_user(root) + config1 = tutor_config.load_minimal(root) + tutor_config.save_config_file(root, config1) + config2 = tutor_config.load_minimal(root) self.assertEqual(config1, config2) @@ -44,28 +45,32 @@ class ConfigTests(unittest.TestCase): tutor_config.utils, "random_string" ) as mock_random_string: mock_random_string.return_value = "abcd" - config1, _defaults1 = tutor_config.load_all(root) + config1 = tutor_config.load_full(root) password1 = config1["MYSQL_ROOT_PASSWORD"] config1.pop("MYSQL_ROOT_PASSWORD") tutor_config.save_config_file(root, config1) mock_random_string.return_value = "efgh" - config2, _defaults2 = tutor_config.load_all(root) + config2 = tutor_config.load_full(root) password2 = config2["MYSQL_ROOT_PASSWORD"] self.assertEqual("abcd", password1) self.assertEqual("efgh", password2) - def test_interactive_load_all(self) -> None: + def test_interactive(self) -> None: + def mock_prompt(*_args: None, **kwargs: str) -> str: + return kwargs["default"] + with tempfile.TemporaryDirectory() as rootdir: - config, defaults = interactive.load_all(rootdir, interactive=False) + with patch.object(click, "prompt", new=mock_prompt): + with patch.object(click, "confirm", new=mock_prompt): + config = interactive.load_user_config(rootdir, interactive=True) self.assertIn("MYSQL_ROOT_PASSWORD", config) self.assertEqual(8, len(get_typed(config, "MYSQL_ROOT_PASSWORD", str))) - self.assertNotIn("LMS_HOST", config) - self.assertEqual("www.myopenedx.com", defaults["LMS_HOST"]) - self.assertEqual("studio.{{ LMS_HOST }}", defaults["CMS_HOST"]) + self.assertEqual("www.myopenedx.com", config["LMS_HOST"]) + self.assertEqual("studio.www.myopenedx.com", config["CMS_HOST"]) def test_is_service_activated(self) -> None: config: Config = {"RUN_SERVICE1": True, "RUN_SERVICE2": False} diff --git a/tests/test_env.py b/tests/test_env.py index 5a03d5d..b0e7395 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -63,7 +63,10 @@ class EnvTests(unittest.TestCase): def test_render_file(self) -> None: config: Config = {} - tutor_config.merge(config, tutor_config.load_defaults()) + tutor_config.update_with_base(config) + tutor_config.update_with_defaults(config) + tutor_config.render_full(config) + config["MYSQL_ROOT_PASSWORD"] = "testpassword" rendered = env.render_file(config, "hooks", "mysql", "init") self.assertIn("testpassword", rendered) @@ -75,10 +78,8 @@ class EnvTests(unittest.TestCase): ) def test_save_full(self) -> None: - defaults = tutor_config.load_defaults() with tempfile.TemporaryDirectory() as root: - config = tutor_config.load_current(root, defaults) - tutor_config.merge(config, defaults) + config = tutor_config.load_full(root) with patch.object(fmt, "STDOUT"): env.save(root, config) self.assertTrue( @@ -86,10 +87,8 @@ class EnvTests(unittest.TestCase): ) def test_save_full_with_https(self) -> None: - defaults = tutor_config.load_defaults() with tempfile.TemporaryDirectory() as root: - config = tutor_config.load_current(root, defaults) - tutor_config.merge(config, defaults) + config = tutor_config.load_full(root) config["ENABLE_HTTPS"] = True with patch.object(fmt, "STDOUT"): env.save(root, config) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 54a98a6..38f756e 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -128,9 +128,6 @@ class PluginsTests(unittest.TestCase): self.assertEqual([], patches) def test_configure(self) -> None: - config: Config = {"ID": "id"} - defaults: Config = {} - class plugin1: config: Config = { "add": {"PARAM1": "value1", "PARAM2": "value2"}, @@ -143,37 +140,31 @@ class PluginsTests(unittest.TestCase): "iter_enabled", return_value=[plugins.BasePlugin("plugin1", plugin1)], ): - tutor_config.load_plugins(config, defaults) + base = tutor_config.get_base({}) + defaults = tutor_config.get_defaults({}) - self.assertEqual( - { - "ID": "id", - "PARAM3": "value3", - "PLUGIN1_PARAM1": "value1", - "PLUGIN1_PARAM2": "value2", - }, - config, - ) - self.assertEqual({"PLUGIN1_PARAM4": "value4"}, defaults) + self.assertEqual(base["PARAM3"], "value3") + self.assertEqual(base["PLUGIN1_PARAM1"], "value1") + self.assertEqual(base["PLUGIN1_PARAM2"], "value2") + self.assertEqual(defaults["PLUGIN1_PARAM4"], "value4") def test_configure_set_does_not_override(self) -> None: - config: Config = {"ID": "oldid"} + config: Config = {"ID1": "oldid"} class plugin1: - config: Config = {"set": {"ID": "newid"}} + config: Config = {"set": {"ID1": "newid", "ID2": "id2"}} with patch.object( plugins.Plugins, "iter_enabled", return_value=[plugins.BasePlugin("plugin1", plugin1)], ): - tutor_config.load_plugins(config, {}) + tutor_config.update_with_base(config) - self.assertEqual({"ID": "oldid"}, config) + self.assertEqual("oldid", config["ID1"]) + self.assertEqual("id2", config["ID2"]) def test_configure_set_random_string(self) -> None: - config: Config = {} - class plugin1: config: Config = {"set": {"PARAM1": "{{ 128|random_string }}"}} @@ -182,12 +173,13 @@ class PluginsTests(unittest.TestCase): "iter_enabled", return_value=[plugins.BasePlugin("plugin1", plugin1)], ): - tutor_config.load_plugins(config, {}) + config = tutor_config.get_base({}) + tutor_config.render_full(config) + self.assertEqual(128, len(get_typed(config, "PARAM1", str))) def test_configure_default_value_with_previous_definition(self) -> None: - config: Config = {} - defaults: Config = {"PARAM1": "value"} + config: Config = {"PARAM1": "value"} class plugin1: config: Config = {"defaults": {"PARAM2": "{{ PARAM1 }}"}} @@ -197,10 +189,10 @@ class PluginsTests(unittest.TestCase): "iter_enabled", return_value=[plugins.BasePlugin("plugin1", plugin1)], ): - tutor_config.load_plugins(config, defaults) - self.assertEqual("{{ PARAM1 }}", defaults["PLUGIN1_PARAM2"]) + tutor_config.update_with_defaults(config) + self.assertEqual("{{ PARAM1 }}", config["PLUGIN1_PARAM2"]) - def test_configure_add_twice(self) -> None: + def test_config_load_from_plugins(self) -> None: config: Config = {} class plugin1: @@ -211,19 +203,12 @@ class PluginsTests(unittest.TestCase): "iter_enabled", return_value=[plugins.BasePlugin("plugin1", plugin1)], ): - tutor_config.load_plugins(config, {}) + tutor_config.update_with_base(config) + tutor_config.update_with_defaults(config) + tutor_config.render_full(config) value1 = get_typed(config, "PLUGIN1_PARAM1", str) - with patch.object( - plugins.Plugins, - "iter_enabled", - return_value=[plugins.BasePlugin("plugin1", plugin1)], - ): - tutor_config.load_plugins(config, {}) - value2 = get_typed(config, "PLUGIN1_PARAM1", str) self.assertEqual(10, len(value1)) - self.assertEqual(10, len(value2)) - self.assertEqual(value1, value2) def test_hooks(self) -> None: class plugin1: diff --git a/tutor/commands/config.py b/tutor/commands/config.py index de68128..964813d 100644 --- a/tutor/commands/config.py +++ b/tutor/commands/config.py @@ -50,16 +50,16 @@ def save( unset_vars: List[str], env_only: bool, ) -> None: - config, defaults = interactive_config.load_all( - context.root, interactive=interactive - ) + config = interactive_config.load_user_config(context.root, interactive=interactive) if set_vars: - tutor_config.merge(config, dict(set_vars), force=True) + for key, value in dict(set_vars).items(): + config[key] = env.render_unknown(config, value) for key in unset_vars: config.pop(key, None) if not env_only: tutor_config.save_config_file(context.root, config) - tutor_config.merge(config, defaults) + + tutor_config.render_full(config) env.save(context.root, config) @@ -78,8 +78,8 @@ def save( def render(context: Context, extra_configs: List[str], src: str, dst: str) -> None: config = tutor_config.load(context.root) for extra_config in extra_configs: - tutor_config.merge( - config, tutor_config.load_config_file(extra_config), force=True + config.update( + env.render_unknown(config, tutor_config.get_yaml_file(extra_config)) ) renderer = env.Renderer(config, [src]) diff --git a/tutor/commands/k8s.py b/tutor/commands/k8s.py index dc97b50..18443bf 100644 --- a/tutor/commands/k8s.py +++ b/tutor/commands/k8s.py @@ -8,11 +8,11 @@ from .. import config as tutor_config from .. import env as tutor_env from .. import exceptions from .. import fmt -from .. import interactive as interactive_config from .. import jobs from .. import serialize from ..types import Config, get_typed from .. import utils +from .config import save as config_save_command from .context import Context @@ -162,9 +162,13 @@ def k8s() -> None: @click.pass_context def quickstart(context: click.Context, non_interactive: bool) -> None: click.echo(fmt.title("Interactive platform configuration")) - config = interactive_config.update( - context.obj.root, interactive=(not non_interactive) + context.invoke( + config_save_command, + interactive=(not non_interactive), + set_vars=[], + unset_vars=[], ) + config = tutor_config.load(context.obj.root) if not config["ENABLE_WEB_PROXY"]: fmt.echo_alert( "Potentially invalid configuration: ENABLE_WEB_PROXY=false\n" diff --git a/tutor/commands/local.py b/tutor/commands/local.py index 8f9b97c..c706561 100644 --- a/tutor/commands/local.py +++ b/tutor/commands/local.py @@ -109,7 +109,7 @@ Your Open edX platform is ready and can be accessed at the following urls: @click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively") @click.pass_context def upgrade(context: click.Context, from_version: str, non_interactive: bool) -> None: - config = tutor_config.load_no_check(context.obj.root) + config = tutor_config.load_full(context.obj.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: diff --git a/tutor/commands/plugins.py b/tutor/commands/plugins.py index 54c9c86..6fe1fad 100644 --- a/tutor/commands/plugins.py +++ b/tutor/commands/plugins.py @@ -28,7 +28,7 @@ def plugins_command() -> None: @click.command(name="list", help="List installed plugins") @click.pass_obj def list_command(context: Context) -> None: - config = tutor_config.load_user(context.root) + config = tutor_config.load_full(context.root) for plugin in plugins.iter_installed(): status = "" if plugins.is_enabled(config, plugin.name) else " (disabled)" print( @@ -42,7 +42,7 @@ def list_command(context: Context) -> None: @click.argument("plugin_names", metavar="plugin", nargs=-1) @click.pass_obj def enable(context: Context, plugin_names: List[str]) -> None: - config = tutor_config.load_user(context.root) + config = tutor_config.load_full(context.root) for plugin in plugin_names: plugins.enable(config, plugin) fmt.echo_info("Plugin {} enabled".format(plugin)) @@ -59,7 +59,7 @@ def enable(context: Context, plugin_names: List[str]) -> None: @click.argument("plugin_names", metavar="plugin", nargs=-1) @click.pass_obj def disable(context: Context, plugin_names: List[str]) -> None: - config = tutor_config.load_user(context.root) + config = tutor_config.load_full(context.root) disable_all = "all" in plugin_names for plugin in plugins.iter_enabled(config): if disable_all or plugin.name in plugin_names: diff --git a/tutor/config.py b/tutor/config.py index 3511ede..4973d24 100644 --- a/tutor/config.py +++ b/tutor/config.py @@ -1,134 +1,178 @@ import os -from typing import Tuple from . import env, exceptions, fmt, plugins, serialize, utils from .types import Config, cast_config -def update(root: str) -> Config: - """ - Load and save the configuration. - """ - config, defaults = load_all(root) - save_config_file(root, config) - merge(config, defaults) - return config - - def load(root: str) -> Config: """ - Load full configuration. This will raise an exception if there is no current - configuration in the project root. + 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. """ - check_existing_config(root) - return load_no_check(root) + 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) + convert_json2yml(root) + return load_full(root) -def load_no_check(root: str) -> Config: - config, defaults = load_all(root) - merge(config, defaults) +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_all(root: str) -> Tuple[Config, Config]: +def load_full(root: str) -> Config: """ - Return: - current (dict): params currently saved in config.yml - defaults (dict): default values of params which might be missing from the - current config + Load a full configuration, with user, base and defaults. """ - defaults = load_defaults() - current = load_current(root, defaults) - return current, defaults + config = get_user(root) + update_with_base(config) + update_with_defaults(config) + render_full(config) + return config -def merge(config: Config, defaults: Config, force: bool = False) -> None: +def update_with_base(config: Config) -> None: """ - Merge default values with user configuration and perform rendering of "{{...}}" - values. + Add base configuration to the config object. + + Note that configuration entries are unrendered at this point. """ - for key, value in defaults.items(): - if force or key not in config: - config[key] = env.render_unknown(config, value) + base = get_base(config) + merge(config, base) -def load_defaults() -> Config: - config = serialize.load(env.read_template_file("config.yml")) +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. + """ + path = config_path(root) + config = {} + if os.path.exists(path): + config = get_yaml_file(path) + upgrade_obsolete(config) + update_with_env(config) + return config + + +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 load_config_file(path: str) -> 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 load_current(root: str, defaults: Config) -> Config: +def merge(config: Config, base: Config) -> None: """ - Load the configuration currently stored on disk. - Note: this modifies the defaults with the plugin default values. + 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. """ - convert_json2yml(root) - config = load_user(root) - load_env(config, defaults) - load_required(config, defaults) - load_plugins(config, defaults) - return config - - -def load_user(root: str) -> Config: - path = config_path(root) - if not os.path.exists(path): - return {} - - config = load_config_file(path) - upgrade_obsolete(config) - return config - - -def load_env(config: Config, defaults: Config) -> None: - for k in defaults.keys(): - env_var = "TUTOR_" + k - if env_var in os.environ: - config[k] = serialize.parse(os.environ[env_var]) - - -def load_required(config: Config, defaults: Config) -> None: - """ - All these keys must be present in the user's config.yml. This includes all values - that are generated once and must be kept after that, such as passwords. - """ - for key in [ - "OPENEDX_SECRET_KEY", - "MYSQL_ROOT_PASSWORD", - "OPENEDX_MYSQL_PASSWORD", - "ID", - "JWT_RSA_PRIVATE_KEY", - ]: + for key, value in base.items(): if key not in config: - config[key] = env.render_unknown(config, defaults[key]) + config[key] = value -def load_plugins(config: Config, defaults: Config) -> None: +def render_full(config: Config) -> None: """ - Add, override and set new defaults from plugins. + 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 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) - if new_key not in config: - config[new_key] = env.render_unknown(config, value) - - # Create new defaults - for key, value in plugin.config_defaults.items(): - defaults[plugin.config_key(key)] = value - - # Set existing config key/values: here, we do not override existing values - # This must come last, as overridden values may depend on plugin defaults - for key, value in plugin.config_set.items(): - if key not in config: - config[key] = env.render_unknown(config, value) + for key, value in config.items(): + config[key] = env.render_unknown(config, value) def is_service_activated(config: Config, service: str) -> bool: @@ -194,7 +238,7 @@ def convert_json2yml(root: str) -> None: root ) ) - config = load_config_file(json_path) + config = get_yaml_file(json_path) save_config_file(root, config) os.remove(json_path) fmt.echo_info( @@ -210,18 +254,5 @@ def save_config_file(root: str, config: Config) -> None: fmt.echo_info("Configuration saved to {}".format(path)) -def check_existing_config(root: str) -> None: - """ - Check there is a configuration on disk and the current environment is up-to-date. - """ - 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) - - def config_path(root: str) -> str: return os.path.join(root, "config.yml") diff --git a/tutor/interactive.py b/tutor/interactive.py index 048e10d..25ba3d9 100644 --- a/tutor/interactive.py +++ b/tutor/interactive.py @@ -1,34 +1,24 @@ -from typing import List, Tuple +from typing import List import click from . import config as tutor_config from . import env, exceptions, fmt -from .__about__ import __version__ from .types import Config, get_typed -def update(root: str, interactive: bool = True) -> Config: - """ - Load and save the configuration. - """ - config, defaults = load_all(root, interactive=interactive) - tutor_config.save_config_file(root, config) - tutor_config.merge(config, defaults) - return config - - -def load_all(root: str, interactive: bool = True) -> Tuple[Config, Config]: +def load_user_config(root: str, interactive: bool = True) -> Config: """ Load configuration and interactively ask questions to collect param values from the user. """ - config, defaults = tutor_config.load_all(root) + config = tutor_config.load_minimal(root) if interactive: - ask_questions(config, defaults) - return config, defaults + ask_questions(config) + return config -def ask_questions(config: Config, defaults: Config) -> None: +def ask_questions(config: Config) -> None: + defaults = tutor_config.get_defaults(config) run_for_prod = config.get("LMS_HOST") != "local.overhang.io" run_for_prod = click.confirm( fmt.question( diff --git a/tutor/templates/config/base.yml b/tutor/templates/config/base.yml new file mode 100644 index 0000000..59a1c98 --- /dev/null +++ b/tutor/templates/config/base.yml @@ -0,0 +1,6 @@ +--- +ID: "{{ 24|random_string }}" +JWT_RSA_PRIVATE_KEY: "{{ 2048|rsa_private_key }}" +MYSQL_ROOT_PASSWORD: "{{ 8|random_string }}" +OPENEDX_MYSQL_PASSWORD: "{{ 8|random_string }}" +OPENEDX_SECRET_KEY: "{{ 24|random_string }}" diff --git a/tutor/templates/config.yml b/tutor/templates/config/defaults.yml similarity index 86% rename from tutor/templates/config.yml rename to tutor/templates/config/defaults.yml index d0e8353..f752209 100644 --- a/tutor/templates/config.yml +++ b/tutor/templates/config/defaults.yml @@ -1,27 +1,10 @@ --- -# These configuration values must be stored in the user's config.yml. -MYSQL_ROOT_PASSWORD: "{{ 8|random_string }}" -OPENEDX_MYSQL_PASSWORD: "{{ 8|random_string }}" -OPENEDX_SECRET_KEY: "{{ 24|random_string }}" -ID: "{{ 24|random_string }}" - +# This file includes all Tutor setting defaults. Settings that do not have a +# default value, such as passwords, should be stored in base.yml. # This must be defined early -LMS_HOST: "www.myopenedx.com" - -# The following are default values -RUN_LMS: true -RUN_CMS: true -RUN_ELASTICSEARCH: true -RUN_MONGODB: true -RUN_MYSQL: true -RUN_REDIS: true -RUN_SMTP: true CADDY_HTTP_PORT: 80 CMS_HOST: "studio.{{ LMS_HOST }}" -PREVIEW_LMS_HOST: "preview.{{ LMS_HOST }}" CONTACT_EMAIL: "contact@{{ LMS_HOST }}" -OPENEDX_AWS_ACCESS_KEY: "" -OPENEDX_AWS_SECRET_ACCESS_KEY: "" DEV_PROJECT_NAME: "{{ TUTOR_APP }}_dev" DOCKER_REGISTRY: "docker.io/" DOCKER_IMAGE_OPENEDX: "{{ DOCKER_REGISTRY }}overhangio/openedx:{{ TUTOR_VERSION }}" @@ -35,7 +18,6 @@ DOCKER_IMAGE_NGINX: "{{ DOCKER_REGISTRY }}nginx:1.21.1" DOCKER_IMAGE_PERMISSIONS: "{{ DOCKER_REGISTRY }}overhangio/openedx-permissions:{{ TUTOR_VERSION }}" DOCKER_IMAGE_REDIS: "{{ DOCKER_REGISTRY }}redis:6.2.6" DOCKER_IMAGE_SMTP: "{{ DOCKER_REGISTRY }}devture/exim-relay:4.94.2-r0-4" -LOCAL_PROJECT_NAME: "{{ TUTOR_APP }}_local" ELASTICSEARCH_HOST: "elasticsearch" ELASTICSEARCH_PORT: 9200 ELASTICSEARCH_SCHEME: "http" @@ -45,14 +27,17 @@ ENABLE_WEB_PROXY: true JWT_COMMON_AUDIENCE: "openedx" JWT_COMMON_ISSUER: "{% if ENABLE_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" +LMS_HOST: "www.myopenedx.com" +LOCAL_PROJECT_NAME: "{{ TUTOR_APP }}_local" MONGODB_HOST: "mongodb" MONGODB_DATABASE: "openedx" MONGODB_PORT: 27017 MONGODB_USERNAME: "" MONGODB_PASSWORD: "" +OPENEDX_AWS_ACCESS_KEY: "" +OPENEDX_AWS_SECRET_ACCESS_KEY: "" OPENEDX_CACHE_REDIS_DB: 1 OPENEDX_CELERY_REDIS_DB: 0 OPENEDX_CMS_UWSGI_WORKERS: 2 @@ -73,6 +58,13 @@ REDIS_HOST: "redis" REDIS_PORT: 6379 REDIS_USERNAME: "" REDIS_PASSWORD: "" +RUN_CMS: true +RUN_ELASTICSEARCH: true +RUN_LMS: true +RUN_MONGODB: true +RUN_MYSQL: true +RUN_REDIS: true +RUN_SMTP: true SMTP_HOST: "smtp" SMTP_PORT: 8025 SMTP_USERNAME: ""