forked from MirrorHub/synapse
When running under docker, we want to use docker's own logging stuff rather than losing the logs somewhere on the container's filesystem, so let's use log configs that spit logs out to stdout instead.
184 lines
5.7 KiB
Python
Executable file
184 lines
5.7 KiB
Python
Executable file
#!/usr/local/bin/python
|
|
|
|
import codecs
|
|
import glob
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
import jinja2
|
|
|
|
|
|
# Utility functions
|
|
def log(txt):
|
|
print(txt, file=sys.stderr)
|
|
|
|
|
|
def error(txt):
|
|
log(txt)
|
|
sys.exit(2)
|
|
|
|
|
|
def convert(src, dst, environ):
|
|
"""Generate a file from a template
|
|
|
|
Args:
|
|
src (str): path to input file
|
|
dst (str): path to file to write
|
|
environ (dict): environment dictionary, for replacement mappings.
|
|
"""
|
|
with open(src) as infile:
|
|
template = infile.read()
|
|
rendered = jinja2.Template(template).render(**environ)
|
|
with open(dst, "w") as outfile:
|
|
outfile.write(rendered)
|
|
|
|
|
|
def generate_config_from_template(environ, ownership):
|
|
"""Generate a homeserver.yaml from environment variables
|
|
|
|
Args:
|
|
environ (dict): environment dictionary
|
|
ownership (str): "<user>:<group>" string which will be used to set
|
|
ownership of the generated configs
|
|
|
|
Returns:
|
|
path to generated config file
|
|
"""
|
|
for v in ("SYNAPSE_SERVER_NAME", "SYNAPSE_REPORT_STATS"):
|
|
if v not in environ:
|
|
error(
|
|
"Environment variable '%s' is mandatory when generating a config "
|
|
"file on-the-fly." % (v,)
|
|
)
|
|
|
|
# populate some params from data files (if they exist, else create new ones)
|
|
environ = environ.copy()
|
|
secrets = {
|
|
"registration": "SYNAPSE_REGISTRATION_SHARED_SECRET",
|
|
"macaroon": "SYNAPSE_MACAROON_SECRET_KEY",
|
|
}
|
|
|
|
for name, secret in secrets.items():
|
|
if secret not in environ:
|
|
filename = "/data/%s.%s.key" % (environ["SYNAPSE_SERVER_NAME"], name)
|
|
|
|
# if the file already exists, load in the existing value; otherwise,
|
|
# generate a new secret and write it to a file
|
|
|
|
if os.path.exists(filename):
|
|
with open(filename) as handle:
|
|
value = handle.read()
|
|
else:
|
|
log("Generating a random secret for {}".format(name))
|
|
value = codecs.encode(os.urandom(32), "hex").decode()
|
|
with open(filename, "w") as handle:
|
|
handle.write(value)
|
|
environ[secret] = value
|
|
|
|
environ["SYNAPSE_APPSERVICES"] = glob.glob("/data/appservices/*.yaml")
|
|
if not os.path.exists("/compiled"):
|
|
os.mkdir("/compiled")
|
|
|
|
config_path = "/compiled/homeserver.yaml"
|
|
|
|
# Convert SYNAPSE_NO_TLS to boolean if exists
|
|
if "SYNAPSE_NO_TLS" in environ:
|
|
tlsanswerstring = str.lower(environ["SYNAPSE_NO_TLS"])
|
|
if tlsanswerstring in ("true", "on", "1", "yes"):
|
|
environ["SYNAPSE_NO_TLS"] = True
|
|
else:
|
|
if tlsanswerstring in ("false", "off", "0", "no"):
|
|
environ["SYNAPSE_NO_TLS"] = False
|
|
else:
|
|
error(
|
|
'Environment variable "SYNAPSE_NO_TLS" found but value "'
|
|
+ tlsanswerstring
|
|
+ '" unrecognized; exiting.'
|
|
)
|
|
|
|
convert("/conf/homeserver.yaml", config_path, environ)
|
|
convert("/conf/log.config", "/compiled/log.config", environ)
|
|
subprocess.check_output(["chown", "-R", ownership, "/data"])
|
|
return config_path
|
|
|
|
|
|
def run_generate_config(environ, ownership):
|
|
"""Run synapse with a --generate-config param to generate a template config file
|
|
|
|
Args:
|
|
environ (dict): env var dict
|
|
ownership (str): "userid:groupid" arg for chmod
|
|
|
|
Never returns.
|
|
"""
|
|
for v in ("SYNAPSE_SERVER_NAME", "SYNAPSE_REPORT_STATS", "SYNAPSE_CONFIG_PATH"):
|
|
if v not in environ:
|
|
error("Environment variable '%s' is mandatory in `generate` mode." % (v,))
|
|
|
|
server_name = environ["SYNAPSE_SERVER_NAME"]
|
|
config_dir = environ.get("SYNAPSE_CONFIG_DIR", "/data")
|
|
data_dir = environ.get("SYNAPSE_DATA_DIR", "/data")
|
|
|
|
# create a suitable log config from our template
|
|
log_config_file = "%s/%s.log.config" % (config_dir, server_name)
|
|
if not os.path.exists(log_config_file):
|
|
log("Creating log config %s" % (log_config_file,))
|
|
convert("/conf/log.config", log_config_file, environ)
|
|
|
|
# make sure that synapse has perms to write to the data dir.
|
|
subprocess.check_output(["chown", ownership, data_dir])
|
|
|
|
args = [
|
|
"python",
|
|
"-m",
|
|
"synapse.app.homeserver",
|
|
"--server-name",
|
|
server_name,
|
|
"--report-stats",
|
|
environ["SYNAPSE_REPORT_STATS"],
|
|
"--config-path",
|
|
environ["SYNAPSE_CONFIG_PATH"],
|
|
"--config-directory",
|
|
config_dir,
|
|
"--data-directory",
|
|
data_dir,
|
|
"--generate-config",
|
|
]
|
|
# log("running %s" % (args, ))
|
|
os.execv("/usr/local/bin/python", args)
|
|
|
|
|
|
def main(args, environ):
|
|
mode = args[1] if len(args) > 1 else None
|
|
ownership = "{}:{}".format(environ.get("UID", 991), environ.get("GID", 991))
|
|
|
|
# In generate mode, generate a configuration and missing keys, then exit
|
|
if mode == "generate":
|
|
return run_generate_config(environ, ownership)
|
|
|
|
# In normal mode, generate missing keys if any, then run synapse
|
|
if "SYNAPSE_CONFIG_PATH" in environ:
|
|
config_path = environ["SYNAPSE_CONFIG_PATH"]
|
|
else:
|
|
config_path = generate_config_from_template(environ, ownership)
|
|
|
|
args = [
|
|
"python",
|
|
"-m",
|
|
"synapse.app.homeserver",
|
|
"--config-path",
|
|
config_path,
|
|
# tell synapse to put any generated keys in /data rather than /compiled
|
|
"--keys-directory",
|
|
"/data",
|
|
]
|
|
|
|
# Generate missing keys and start synapse
|
|
subprocess.check_output(args + ["--generate-keys"])
|
|
os.execv("/sbin/su-exec", ["su-exec", ownership] + args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv, os.environ)
|