Add function to load config without generating it

Renames ``load_config`` to ``load_or_generate_config``
Adds a method called ``load_config`` that just loads the
config.

The main synapse.app.homeserver will continue to use
``load_or_generate_config`` to retain backwards compat.
However new worker processes can use ``load_config`` to
load the config avoiding some of the cruft needed to generate
the config.

As the new ``load_config`` method is expected to be used by new
configs it removes support for the legacy commandline overrides
that ``load_or_generate_config`` supports
This commit is contained in:
Mark Haines 2016-06-09 18:50:38 +01:00
parent 5ee5b655b2
commit 7dbb473339
4 changed files with 125 additions and 47 deletions

View File

@ -266,10 +266,9 @@ def setup(config_options):
HomeServer HomeServer
""" """
try: try:
config = HomeServerConfig.load_config( config = HomeServerConfig.load_or_generate_config(
"Synapse Homeserver", "Synapse Homeserver",
config_options, config_options,
generate_section="Homeserver"
) )
except ConfigError as e: except ConfigError as e:
sys.stderr.write("\n" + e.message + "\n") sys.stderr.write("\n" + e.message + "\n")

View File

@ -157,9 +157,40 @@ class Config(object):
return default_config, config return default_config, config
@classmethod @classmethod
def load_config(cls, description, argv, generate_section=None): def load_config(cls, description, argv):
obj = cls() config_parser = argparse.ArgumentParser(
description=description,
)
config_parser.add_argument(
"-c", "--config-path",
action="append",
metavar="CONFIG_FILE",
help="Specify config file. Can be given multiple times and"
" may specify directories containing *.yaml files."
)
config_parser.add_argument(
"--keys-directory",
metavar="DIRECTORY",
help="Where files such as certs and signing keys are stored when"
" their location is given explicitly in the config."
" Defaults to the directory containing the last config file",
)
config_args = config_parser.parse_args(argv)
config_files = find_config_files(search_paths=config_args.config_path)
obj = cls()
obj.read_config_files(
config_files,
keys_directory=config_args.keys_directory,
generate_keys=False,
)
return obj
@classmethod
def load_or_generate_config(cls, description, argv):
config_parser = argparse.ArgumentParser(add_help=False) config_parser = argparse.ArgumentParser(add_help=False)
config_parser.add_argument( config_parser.add_argument(
"-c", "--config-path", "-c", "--config-path",
@ -176,7 +207,7 @@ class Config(object):
config_parser.add_argument( config_parser.add_argument(
"--report-stats", "--report-stats",
action="store", action="store",
help="Stuff", help="Whether the generated config reports anonymized usage statistics",
choices=["yes", "no"] choices=["yes", "no"]
) )
config_parser.add_argument( config_parser.add_argument(
@ -197,36 +228,11 @@ class Config(object):
) )
config_args, remaining_args = config_parser.parse_known_args(argv) config_args, remaining_args = config_parser.parse_known_args(argv)
config_files = find_config_files(search_paths=config_args.config_path)
generate_keys = config_args.generate_keys generate_keys = config_args.generate_keys
config_files = [] obj = cls()
if config_args.config_path:
for config_path in config_args.config_path:
if os.path.isdir(config_path):
# We accept specifying directories as config paths, we search
# inside that directory for all files matching *.yaml, and then
# we apply them in *sorted* order.
files = []
for entry in os.listdir(config_path):
entry_path = os.path.join(config_path, entry)
if not os.path.isfile(entry_path):
print (
"Found subdirectory in config directory: %r. IGNORING."
) % (entry_path, )
continue
if not entry.endswith(".yaml"):
print (
"Found file in config directory that does not"
" end in '.yaml': %r. IGNORING."
) % (entry_path, )
continue
files.append(entry_path)
config_files.extend(sorted(files))
else:
config_files.append(config_path)
if config_args.generate_config: if config_args.generate_config:
if config_args.report_stats is None: if config_args.report_stats is None:
@ -299,28 +305,43 @@ class Config(object):
" -c CONFIG-FILE\"" " -c CONFIG-FILE\""
) )
if config_args.keys_directory: obj.read_config_files(
config_dir_path = config_args.keys_directory config_files,
else: keys_directory=config_args.keys_directory,
config_dir_path = os.path.dirname(config_args.config_path[-1]) generate_keys=generate_keys,
config_dir_path = os.path.abspath(config_dir_path) )
if generate_keys:
return None
obj.invoke_all("read_arguments", args)
return obj
def read_config_files(self, config_files, keys_directory=None,
generate_keys=False):
if not keys_directory:
keys_directory = os.path.dirname(config_files[-1])
config_dir_path = os.path.abspath(keys_directory)
specified_config = {} specified_config = {}
for config_file in config_files: for config_file in config_files:
yaml_config = cls.read_config_file(config_file) yaml_config = self.read_config_file(config_file)
specified_config.update(yaml_config) specified_config.update(yaml_config)
if "server_name" not in specified_config: if "server_name" not in specified_config:
raise ConfigError(MISSING_SERVER_NAME) raise ConfigError(MISSING_SERVER_NAME)
server_name = specified_config["server_name"] server_name = specified_config["server_name"]
_, config = obj.generate_config( _, config = self.generate_config(
config_dir_path=config_dir_path, config_dir_path=config_dir_path,
server_name=server_name, server_name=server_name,
is_generating_file=False, is_generating_file=False,
) )
config.pop("log_config") config.pop("log_config")
config.update(specified_config) config.update(specified_config)
if "report_stats" not in config: if "report_stats" not in config:
raise ConfigError( raise ConfigError(
MISSING_REPORT_STATS_CONFIG_INSTRUCTIONS + "\n" + MISSING_REPORT_STATS_CONFIG_INSTRUCTIONS + "\n" +
@ -328,11 +349,51 @@ class Config(object):
) )
if generate_keys: if generate_keys:
obj.invoke_all("generate_files", config) self.invoke_all("generate_files", config)
return return
obj.invoke_all("read_config", config) self.invoke_all("read_config", config)
obj.invoke_all("read_arguments", args)
return obj def find_config_files(search_paths):
"""Finds config files using a list of search paths. If a path is a file
then that file path is added to the list. If a search path is a directory
then all the "*.yaml" files in that directory are added to the list in
sorted order.
Args:
search_paths(list(str)): A list of paths to search.
Returns:
list(str): A list of file paths.
"""
config_files = []
if search_paths:
for config_path in search_paths:
if os.path.isdir(config_path):
# We accept specifying directories as config paths, we search
# inside that directory for all files matching *.yaml, and then
# we apply them in *sorted* order.
files = []
for entry in os.listdir(config_path):
entry_path = os.path.join(config_path, entry)
if not os.path.isfile(entry_path):
print (
"Found subdirectory in config directory: %r. IGNORING."
) % (entry_path, )
continue
if not entry.endswith(".yaml"):
print (
"Found file in config directory that does not"
" end in '.yaml': %r. IGNORING."
) % (entry_path, )
continue
files.append(entry_path)
config_files.extend(sorted(files))
else:
config_files.append(config_path)
return config_files

View File

@ -30,7 +30,7 @@ class ConfigGenerationTestCase(unittest.TestCase):
shutil.rmtree(self.dir) shutil.rmtree(self.dir)
def test_generate_config_generates_files(self): def test_generate_config_generates_files(self):
HomeServerConfig.load_config("", [ HomeServerConfig.load_or_generate_config("", [
"--generate-config", "--generate-config",
"-c", self.file, "-c", self.file,
"--report-stats=yes", "--report-stats=yes",

View File

@ -34,6 +34,8 @@ class ConfigLoadingTestCase(unittest.TestCase):
self.generate_config_and_remove_lines_containing("server_name") self.generate_config_and_remove_lines_containing("server_name")
with self.assertRaises(Exception): with self.assertRaises(Exception):
HomeServerConfig.load_config("", ["-c", self.file]) HomeServerConfig.load_config("", ["-c", self.file])
with self.assertRaises(Exception):
HomeServerConfig.load_or_generate_config("", ["-c", self.file])
def test_generates_and_loads_macaroon_secret_key(self): def test_generates_and_loads_macaroon_secret_key(self):
self.generate_config() self.generate_config()
@ -54,11 +56,24 @@ class ConfigLoadingTestCase(unittest.TestCase):
"was: %r" % (config.macaroon_secret_key,) "was: %r" % (config.macaroon_secret_key,)
) )
config = HomeServerConfig.load_or_generate_config("", ["-c", self.file])
self.assertTrue(
hasattr(config, "macaroon_secret_key"),
"Want config to have attr macaroon_secret_key"
)
if len(config.macaroon_secret_key) < 5:
self.fail(
"Want macaroon secret key to be string of at least length 5,"
"was: %r" % (config.macaroon_secret_key,)
)
def test_load_succeeds_if_macaroon_secret_key_missing(self): def test_load_succeeds_if_macaroon_secret_key_missing(self):
self.generate_config_and_remove_lines_containing("macaroon") self.generate_config_and_remove_lines_containing("macaroon")
config1 = HomeServerConfig.load_config("", ["-c", self.file]) config1 = HomeServerConfig.load_config("", ["-c", self.file])
config2 = HomeServerConfig.load_config("", ["-c", self.file]) config2 = HomeServerConfig.load_config("", ["-c", self.file])
config3 = HomeServerConfig.load_or_generate_config("", ["-c", self.file])
self.assertEqual(config1.macaroon_secret_key, config2.macaroon_secret_key) self.assertEqual(config1.macaroon_secret_key, config2.macaroon_secret_key)
self.assertEqual(config1.macaroon_secret_key, config3.macaroon_secret_key)
def test_disable_registration(self): def test_disable_registration(self):
self.generate_config() self.generate_config()
@ -70,14 +85,17 @@ class ConfigLoadingTestCase(unittest.TestCase):
config = HomeServerConfig.load_config("", ["-c", self.file]) config = HomeServerConfig.load_config("", ["-c", self.file])
self.assertFalse(config.enable_registration) self.assertFalse(config.enable_registration)
config = HomeServerConfig.load_or_generate_config("", ["-c", self.file])
self.assertFalse(config.enable_registration)
# Check that either config value is clobbered by the command line. # Check that either config value is clobbered by the command line.
config = HomeServerConfig.load_config("", [ config = HomeServerConfig.load_or_generate_config("", [
"-c", self.file, "--enable-registration" "-c", self.file, "--enable-registration"
]) ])
self.assertTrue(config.enable_registration) self.assertTrue(config.enable_registration)
def generate_config(self): def generate_config(self):
HomeServerConfig.load_config("", [ HomeServerConfig.load_or_generate_config("", [
"--generate-config", "--generate-config",
"-c", self.file, "-c", self.file,
"--report-stats=yes", "--report-stats=yes",