mirror of
https://github.com/ChristianLight/tutor.git
synced 2024-11-13 16:56:29 +00:00
ea2dd7c4fb
We no longer run the `configure` script on the host. Instead, we run a container that generates the configuration files. This opens the way for more complex configuration templates that would be written in jinja2. More complex templates are required for feature flags, such as SSL, XQUEUE, etc.
173 lines
5.4 KiB
Python
Executable File
173 lines
5.4 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
# coding: utf8
|
|
import argparse
|
|
import codecs
|
|
import json
|
|
import os
|
|
import random
|
|
import string
|
|
import sys
|
|
|
|
from collections import OrderedDict
|
|
|
|
|
|
class Configurator:
|
|
|
|
def __init__(self, **default_overrides):
|
|
self.__values = OrderedDict()
|
|
self.__default_values = default_overrides
|
|
try:
|
|
self.__input = raw_input
|
|
except NameError:
|
|
self.__input = input
|
|
|
|
def as_dict(self):
|
|
all_values = self.__values.copy()
|
|
for key, value in self.__default_values.items():
|
|
all_values.setdefault(key, value)
|
|
return all_values
|
|
|
|
def mute(self):
|
|
self.__input = None
|
|
|
|
def add(self, name, question="", default=""):
|
|
default = self.__default_values.get(name, default)
|
|
value = default
|
|
if question:
|
|
message = question + " (default: \"{}\"): ".format(default)
|
|
value = self.ask(message, default)
|
|
self.set(name, value)
|
|
|
|
return self
|
|
|
|
def add_boolean(self, name, question, default=True):
|
|
default = self.__default_values.get(name, default)
|
|
message = question + " ({}) ".format("Y/n" if default else "y/N")
|
|
value = None
|
|
while value is None:
|
|
answer = self.ask(message, default)
|
|
value = {
|
|
"y": True,
|
|
"n": False,
|
|
default: default,
|
|
}.get(answer)
|
|
self.set(name, value)
|
|
return self
|
|
|
|
def ask(self, message, default):
|
|
if self.__input:
|
|
return self.__input(message) or default
|
|
return default
|
|
|
|
def get(self, name):
|
|
return self.__values.get(name)
|
|
|
|
def set(self, name, value):
|
|
self.__values[name] = value
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser("Config file generator for Open edX")
|
|
parser.add_argument('-c', '--config', default=os.path.join("/", "openedx", "config", "config.json"),
|
|
help="Load default values from this file. Config values will be saved there.")
|
|
subparsers = parser.add_subparsers()
|
|
|
|
parser_interactive = subparsers.add_parser('interactive')
|
|
parser_interactive.add_argument('-s', '--silent', action='store_true',
|
|
help=(
|
|
"Be silent and accept all default values. "
|
|
"This is good for debugging and automation, but "
|
|
"probably not what you want"
|
|
))
|
|
parser_interactive.set_defaults(func=interactive)
|
|
|
|
parser_substitute = subparsers.add_parser('substitute')
|
|
parser_substitute.add_argument('--delimiter', default='$', help="Template file delimiter")
|
|
parser_substitute.add_argument('src', help="Template source file")
|
|
parser_substitute.add_argument('dst', help="Destination configuration file")
|
|
parser_substitute.set_defaults(func=substitute)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Load defaults
|
|
defaults = {}
|
|
if os.path.exists(args.config):
|
|
with open(args.config) as f:
|
|
defaults = json.load(f)
|
|
configurator = Configurator(**defaults)
|
|
|
|
args.func(configurator, args)
|
|
|
|
|
|
def interactive(configurator, args):
|
|
if args.silent:
|
|
configurator.mute()
|
|
configurator.add(
|
|
'LMS_HOST', "Your website domain name for students (LMS).", 'www.myopenedx.com'
|
|
).add(
|
|
'CMS_HOST', "Your website domain name for teachers (CMS).", 'studio.myopenedx.com'
|
|
).add(
|
|
'PLATFORM_NAME', "Platform name/title", "My Open edX"
|
|
).add(
|
|
'SECRET_KEY', "", random_string(24)
|
|
).add(
|
|
'MYSQL_DATABASE', "", 'openedx'
|
|
).add(
|
|
'MYSQL_USERNAME', "", 'openedx'
|
|
).add(
|
|
'MYSQL_PASSWORD', "", random_string(8),
|
|
).add(
|
|
'MONGODB_DATABASE', "", 'openedx'
|
|
).add(
|
|
'XQUEUE_AUTH_USERNAME', "", 'lms'
|
|
).add(
|
|
'XQUEUE_AUTH_PASSWORD', "", random_string(8),
|
|
).add(
|
|
'XQUEUE_MYSQL_DATABASE', "", 'xqueue',
|
|
).add(
|
|
'XQUEUE_MYSQL_USERNAME', "", 'xqueue',
|
|
).add(
|
|
'XQUEUE_MYSQL_PASSWORD', "", random_string(8),
|
|
).add(
|
|
'XQUEUE_SECRET_KEY', "", random_string(24),
|
|
)
|
|
|
|
# Save values
|
|
with open(args.config, 'w') as f:
|
|
json.dump(configurator.as_dict(), f, sort_keys=True, indent=4)
|
|
print("\nConfiguration values were saved to ", args.config)
|
|
|
|
|
|
def substitute(configurator, args):
|
|
with codecs.open(args.src, encoding='utf-8') as fi:
|
|
template = template_class(args.delimiter)(fi.read())
|
|
try:
|
|
substituted = template.substitute(**configurator.as_dict())
|
|
except KeyError as e:
|
|
sys.stderr.write("ERROR Missing config value '{}' for template {}\n".format(e.args[0], args.src))
|
|
sys.exit(1)
|
|
|
|
with codecs.open(args.dst, encoding='utf-8', mode='w') as fo:
|
|
fo.write(substituted)
|
|
|
|
print("Generated file {} from template {}".format(args.dst, args.src))
|
|
|
|
|
|
def template_class(user_delimiter='$'):
|
|
"""
|
|
The default delimiter of the python Template class is '$'. Here, we
|
|
generate a Template class with a custom delimiter. This cannot be done
|
|
after the class creation because the Template metaclass uses the delimiter
|
|
value.
|
|
"""
|
|
class Template(string.Template):
|
|
delimiter = user_delimiter
|
|
return Template
|
|
|
|
|
|
def random_string(length):
|
|
return "".join([random.choice(string.ascii_letters + string.digits) for _ in range(length)])
|
|
|
|
if __name__ == '__main__':
|
|
main()
|