6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-11-13 16:56:29 +00:00
tutor/configurator/bin/configure.py
Régis Behmo ea2dd7c4fb Generate configuration from Docker
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.
2018-08-16 14:42:10 +02:00

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()