6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-11-11 15:51:00 +00:00
tutor/configurator/bin/configure.py
Régis Behmo 04a0fb5902 Simplify configurator
Template files are now directly loaded in the configurator container, so
that it is possible to run the configurator container directly, outside
of this project.
2018-09-15 17:22:02 +02:00

197 lines
6.2 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
import jinja2
class Configurator:
def __init__(self, **default_overrides):
"""
Default values are read, in decreasing order of priority, from:
- SETTING_<name> environment variable
- Existing config file (in `default_overrides`)
- Value passed to add()
"""
self.__values = OrderedDict()
self.__default_values = default_overrides
try:
self.__input = raw_input
except NameError:
self.__input = input
def as_dict(self):
return self.__values
def mute(self):
self.__input = None
def add(self, name, question="", default=""):
default = self.get_default_value(name, default)
message = question + " (default: \"{}\"): ".format(default) if question else None
value = self.ask(message, default)
self.set(name, value)
return self
def add_bool(self, name, question="", default=False):
self.add(name, question=question, default=default)
value = self.get(name)
if value in [1, '1']:
return self.set(name, True)
if value in [0, '0', '']:
return self.set(name, False)
if value in [True, False]:
return self
return self.set(name, bool(value))
def get_default_value(self, name, default):
setting_name = 'SETTING_' + name.upper()
if os.environ.get(setting_name):
return os.environ[setting_name]
if name in self.__default_values:
return self.__default_values[name]
return default
def ask(self, message, default):
if self.__input and message:
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
return self
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('src', help="Template source directory")
parser_substitute.add_argument('dst', help="Destination configuration directory")
parser_substitute.set_defaults(func=substitute)
args = parser.parse_args()
args.func(args)
def load_config(args):
if os.path.exists(args.config):
with open(args.config) as f:
return json.load(f)
return {}
def interactive(args):
print("\n====================================")
print(" Interactive configuration ")
print("====================================")
configurator = Configurator(**load_config(args))
if args.silent or os.environ.get('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(
'CONTACT_EMAIL', "Public contact email address", 'contact@' + configurator.get('LMS_HOST')
).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)
).add_bool(
'ACTIVATE_HTTPS', "", False
).add_bool(
'ACTIVATE_XQUEUE', "", False
)
# 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(args):
config = load_config(args)
for root, _, filenames in os.walk(args.src):
for filename in filenames:
if filename.startswith('.'):
# Skip hidden files, such as files generated by the IDE
continue
src_file = os.path.join(root, filename)
dst_file = os.path.join(args.dst, os.path.relpath(src_file, args.src))
substitute_file(config, src_file, dst_file)
def substitute_file(config, src, dst):
with codecs.open(src, encoding='utf-8') as fi:
template = jinja2.Template(fi.read(), undefined=jinja2.StrictUndefined)
try:
substituted = template.render(**config)
except jinja2.exceptions.UndefinedError as e:
sys.stderr.write("ERROR Missing config value '{}' for template {}\n".format(e.args[0], src))
sys.exit(1)
dst_dir = os.path.dirname(dst)
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
with codecs.open(dst, encoding='utf-8', mode='w') as fo:
fo.write(substituted)
# Set same permissions as original file
os.chmod(dst, os.stat(src).st_mode)
print("Generated file {} from template {}".format(dst, src))
def random_string(length):
return "".join([random.choice(string.ascii_letters + string.digits) for _ in range(length)])
if __name__ == '__main__':
main()