2017-12-26 00:16:35 +00:00
|
|
|
#! /usr/bin/env python
|
|
|
|
# coding: utf8
|
|
|
|
from __future__ import unicode_literals, print_function
|
|
|
|
import argparse
|
|
|
|
import codecs
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import string
|
|
|
|
import sys
|
|
|
|
|
|
|
|
class Configurator(object):
|
|
|
|
|
|
|
|
def __init__(self, silent=False, **default_overrides):
|
|
|
|
self.__values = []
|
|
|
|
self.__default_overrides = default_overrides
|
|
|
|
if silent:
|
|
|
|
self.__input = None
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
self.__input = raw_input
|
|
|
|
except NameError:
|
|
|
|
self.__input = input
|
|
|
|
|
|
|
|
def as_dict(self):
|
|
|
|
return dict(self.__values)
|
|
|
|
|
2018-06-02 01:29:53 +00:00
|
|
|
def add(self, name, question="", default=""):
|
2017-12-26 00:16:35 +00:00
|
|
|
default = self.__default_overrides.get(name, default)
|
2018-06-02 01:29:53 +00:00
|
|
|
value = default
|
|
|
|
if question:
|
|
|
|
message = question + " (default: \"{}\"): ".format(default)
|
|
|
|
value = self.ask(message, default)
|
2017-12-26 00:16:35 +00:00
|
|
|
self.set(name, value)
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
def add_boolean(self, name, question, default=True):
|
|
|
|
default = self.__default_overrides.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):
|
|
|
|
for key, value in self.__values:
|
|
|
|
if key == name:
|
|
|
|
return value
|
|
|
|
return None
|
|
|
|
|
|
|
|
def set(self, name, value):
|
|
|
|
self.__values.append((name, value))
|
|
|
|
|
|
|
|
|
|
|
|
def substitute(src, dst, delimiter='$', **values):
|
|
|
|
Template = template_class(delimiter)
|
|
|
|
|
|
|
|
with codecs.open(src, encoding='utf-8') as fi:
|
|
|
|
template = Template(fi.read())
|
|
|
|
try:
|
|
|
|
substituted = template.substitute(**values)
|
|
|
|
except KeyError as e:
|
|
|
|
sys.stderr.write("ERROR Missing config value '{}' for template {}\n".format(e.args[0], src))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
with open(dst, 'w') as fo:
|
|
|
|
fo.write(substituted)
|
|
|
|
|
|
|
|
print("Generated config file {} (from template {})".format(dst, 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 main():
|
2018-05-16 17:02:44 +00:00
|
|
|
# Hack to handle older config.json files that used to be in a different location
|
|
|
|
# TODO remove me
|
2018-06-08 21:39:34 +00:00
|
|
|
config_path = os.path.join("config", "config.json")
|
|
|
|
deprecated_paths = [
|
|
|
|
"config.json",
|
|
|
|
os.path.join("config", "openedx", "config.json"),
|
|
|
|
]
|
|
|
|
for deprecated_path in deprecated_paths:
|
|
|
|
if os.path.exists(deprecated_path):
|
|
|
|
os.rename(deprecated_path, config_path)
|
2018-05-16 17:02:44 +00:00
|
|
|
|
2017-12-26 00:16:35 +00:00
|
|
|
parser = argparse.ArgumentParser("Config file generator for Open edX")
|
2018-06-08 21:39:34 +00:00
|
|
|
parser.add_argument('-c', '--config', default=config_path,
|
2017-12-26 00:16:35 +00:00
|
|
|
help="Load default values from this file. Config values will be saved there.")
|
|
|
|
parser.add_argument('-s', '--silent', action='store_true',
|
|
|
|
help=(
|
|
|
|
"Be silent and accept all default values. "
|
|
|
|
"This is good for debugging, but probably not what you want"
|
|
|
|
))
|
|
|
|
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(silent=args.silent, **defaults).add(
|
2018-06-08 21:21:08 +00:00
|
|
|
'LMS_HOST', "Your website domain name for students (LMS).", 'www.myopenedx.com'
|
2017-12-26 00:16:35 +00:00
|
|
|
).add(
|
2018-06-08 21:21:08 +00:00
|
|
|
'CMS_HOST', "Your website domain name for teachers (CMS).", 'studio.myopenedx.com'
|
2017-12-26 00:16:35 +00:00
|
|
|
).add(
|
|
|
|
'PLATFORM_NAME', "Platform name/title", "My Open edX"
|
|
|
|
).add(
|
2018-06-02 01:29:53 +00:00
|
|
|
'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),
|
2017-12-26 00:16:35 +00:00
|
|
|
).add(
|
2018-06-02 01:29:53 +00:00
|
|
|
'XQUEUE_MYSQL_DATABASE', "", 'xqueue',
|
2017-12-26 00:16:35 +00:00
|
|
|
).add(
|
2018-06-02 01:29:53 +00:00
|
|
|
'XQUEUE_MYSQL_USERNAME', "", 'xqueue',
|
2017-12-26 00:16:35 +00:00
|
|
|
).add(
|
2018-06-02 01:29:53 +00:00
|
|
|
'XQUEUE_MYSQL_PASSWORD', "", random_string(8),
|
|
|
|
).add(
|
|
|
|
'XQUEUE_SECRET_KEY', "", random_string(24),
|
2017-12-26 00:16:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
# Open edX
|
|
|
|
substitute(
|
2018-05-16 17:02:44 +00:00
|
|
|
os.path.join('config', 'openedx', 'templates', 'lms.env.json.templ'),
|
|
|
|
os.path.join('config', 'openedx', 'lms.env.json'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
|
|
|
substitute(
|
2018-05-16 17:02:44 +00:00
|
|
|
os.path.join('config', 'openedx', 'templates', 'cms.env.json.templ'),
|
|
|
|
os.path.join('config', 'openedx', 'cms.env.json'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
|
|
|
substitute(
|
2018-05-16 17:02:44 +00:00
|
|
|
os.path.join('config', 'openedx', 'templates', 'lms.auth.json.templ'),
|
|
|
|
os.path.join('config', 'openedx', 'lms.auth.json'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
|
|
|
substitute(
|
2018-05-16 17:02:44 +00:00
|
|
|
os.path.join('config', 'openedx', 'templates', 'cms.auth.json.templ'),
|
|
|
|
os.path.join('config', 'openedx', 'cms.auth.json'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
|
|
|
substitute(
|
2018-06-02 01:29:53 +00:00
|
|
|
os.path.join('config', 'openedx', 'templates', 'provision.sh.templ'),
|
|
|
|
os.path.join('config', 'openedx', 'provision.sh'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
2018-06-02 01:29:53 +00:00
|
|
|
|
|
|
|
# Xqueue
|
2017-12-26 00:16:35 +00:00
|
|
|
substitute(
|
2018-06-02 01:29:53 +00:00
|
|
|
os.path.join('config', 'xqueue', 'templates', 'universal.py.templ'),
|
|
|
|
os.path.join('config', 'xqueue', 'universal.py'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
2018-06-02 01:29:53 +00:00
|
|
|
|
|
|
|
# MySQL
|
2017-12-26 00:16:35 +00:00
|
|
|
substitute(
|
2018-06-02 01:29:53 +00:00
|
|
|
os.path.join('config', 'mysql', 'templates', 'auth.env.templ'),
|
|
|
|
os.path.join('config', 'mysql', 'auth.env'),
|
2017-12-26 00:16:35 +00:00
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
|
|
|
|
|
|
|
# Nginx
|
|
|
|
# We need a different delimiter in nginx config files, because the '$' sign
|
|
|
|
# is widely used there
|
|
|
|
substitute(
|
2018-05-16 17:02:44 +00:00
|
|
|
os.path.join('config', 'nginx', 'templates', 'lms.conf.templ'),
|
|
|
|
os.path.join('config', 'nginx', 'lms.conf'),
|
2017-12-26 00:16:35 +00:00
|
|
|
delimiter='£', **configurator.as_dict()
|
|
|
|
)
|
|
|
|
substitute(
|
2018-05-16 17:02:44 +00:00
|
|
|
os.path.join('config', 'nginx', 'templates', 'cms.conf.templ'),
|
|
|
|
os.path.join('config', 'nginx', 'cms.conf'),
|
2017-12-26 00:16:35 +00:00
|
|
|
delimiter='£', **configurator.as_dict()
|
|
|
|
)
|
|
|
|
|
2018-05-27 19:29:14 +00:00
|
|
|
# Android
|
|
|
|
substitute(
|
|
|
|
os.path.join('config', 'android', 'templates', 'universal.yaml.templ'),
|
|
|
|
os.path.join('config', 'android', 'universal.yaml'),
|
|
|
|
**configurator.as_dict()
|
|
|
|
)
|
|
|
|
|
2018-05-16 17:02:44 +00:00
|
|
|
print("\nConfiguration files were successfuly generated. You may now run the app containers.")
|
2017-12-26 00:16:35 +00:00
|
|
|
|
|
|
|
|
2018-06-02 01:29:53 +00:00
|
|
|
def random_string(length):
|
|
|
|
return "".join([random.choice(string.ascii_letters + string.digits) for _ in range(length)])
|
|
|
|
|
2017-12-26 00:16:35 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|