Generate missing configuration files at startup (#13615)

If things like the signing key file are missing, let's just try to generate
them on startup.

Again, this is useful for k8s-like deployments where we just want to generate
keys on the first run.
pull/13645/head
Richard van der Hoff 2022-08-26 12:26:06 +01:00 committed by GitHub
parent 998e211836
commit 5e5c8150d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 14 deletions

View File

@ -0,0 +1 @@
Change the default startup behaviour so that any missing "additional" configuration files (signing key, etc) are generated automatically.

View File

@ -2139,6 +2139,9 @@ allows the shared secret to be specified in an external file.
The file should be a plain text file, containing only the shared secret. The file should be a plain text file, containing only the shared secret.
If this file does not exist, Synapse will create a new signing
key on startup and store it in this file.
Example configuration: Example configuration:
```yaml ```yaml
registration_shared_secret_file: /path/to/secrets/file registration_shared_secret_file: /path/to/secrets/file
@ -2555,7 +2558,10 @@ Config options relating to signing keys
--- ---
### `signing_key_path` ### `signing_key_path`
Path to the signing key to sign messages with. Path to the signing key to sign events and federation requests with.
*New in Synapse 1.67*: If this file does not exist, Synapse will create a new signing
key on startup and store it in this file.
Example configuration: Example configuration:
```yaml ```yaml

View File

@ -20,6 +20,7 @@ import logging
import os import os
import re import re
from collections import OrderedDict from collections import OrderedDict
from enum import Enum, auto
from hashlib import sha256 from hashlib import sha256
from textwrap import dedent from textwrap import dedent
from typing import ( from typing import (
@ -603,18 +604,44 @@ class RootConfig:
" may specify directories containing *.yaml files.", " may specify directories containing *.yaml files.",
) )
generate_group = parser.add_argument_group("Config generation") # we nest the mutually-exclusive group inside another group so that the help
generate_group.add_argument( # text shows them in their own group.
"--generate-config", generate_mode_group = parser.add_argument_group(
action="store_true", "Config generation mode",
help="Generate a config file, then exit.",
) )
generate_group.add_argument( generate_mode_exclusive = generate_mode_group.add_mutually_exclusive_group()
generate_mode_exclusive.add_argument(
# hidden option to make the type and default work
"--generate-mode",
help=argparse.SUPPRESS,
type=_ConfigGenerateMode,
default=_ConfigGenerateMode.GENERATE_MISSING_AND_RUN,
)
generate_mode_exclusive.add_argument(
"--generate-config",
help="Generate a config file, then exit.",
action="store_const",
const=_ConfigGenerateMode.GENERATE_EVERYTHING_AND_EXIT,
dest="generate_mode",
)
generate_mode_exclusive.add_argument(
"--generate-missing-configs", "--generate-missing-configs",
"--generate-keys", "--generate-keys",
action="store_true",
help="Generate any missing additional config files, then exit.", help="Generate any missing additional config files, then exit.",
action="store_const",
const=_ConfigGenerateMode.GENERATE_MISSING_AND_EXIT,
dest="generate_mode",
) )
generate_mode_exclusive.add_argument(
"--generate-missing-and-run",
help="Generate any missing additional config files, then run. This is the "
"default behaviour.",
action="store_const",
const=_ConfigGenerateMode.GENERATE_MISSING_AND_RUN,
dest="generate_mode",
)
generate_group = parser.add_argument_group("Details for --generate-config")
generate_group.add_argument( generate_group.add_argument(
"-H", "--server-name", help="The server name to generate a config file for." "-H", "--server-name", help="The server name to generate a config file for."
) )
@ -670,11 +697,12 @@ class RootConfig:
config_dir_path = os.path.abspath(config_dir_path) config_dir_path = os.path.abspath(config_dir_path)
data_dir_path = os.getcwd() data_dir_path = os.getcwd()
generate_missing_configs = config_args.generate_missing_configs
obj = cls(config_files) obj = cls(config_files)
if config_args.generate_config: if (
config_args.generate_mode
== _ConfigGenerateMode.GENERATE_EVERYTHING_AND_EXIT
):
if config_args.report_stats is None: if config_args.report_stats is None:
parser.error( parser.error(
"Please specify either --report-stats=yes or --report-stats=no\n\n" "Please specify either --report-stats=yes or --report-stats=no\n\n"
@ -732,11 +760,14 @@ class RootConfig:
) )
% (config_path,) % (config_path,)
) )
generate_missing_configs = True
config_dict = read_config_files(config_files) config_dict = read_config_files(config_files)
if generate_missing_configs: obj.generate_missing_files(config_dict, config_dir_path)
obj.generate_missing_files(config_dict, config_dir_path)
if config_args.generate_mode in (
_ConfigGenerateMode.GENERATE_EVERYTHING_AND_EXIT,
_ConfigGenerateMode.GENERATE_MISSING_AND_EXIT,
):
return None return None
obj.parse_config_dict( obj.parse_config_dict(
@ -965,6 +996,12 @@ def read_file(file_path: Any, config_path: Iterable[str]) -> str:
raise ConfigError("Error accessing file %r" % (file_path,), config_path) from e raise ConfigError("Error accessing file %r" % (file_path,), config_path) from e
class _ConfigGenerateMode(Enum):
GENERATE_MISSING_AND_RUN = auto()
GENERATE_MISSING_AND_EXIT = auto()
GENERATE_EVERYTHING_AND_EXIT = auto()
__all__ = [ __all__ = [
"Config", "Config",
"RootConfig", "RootConfig",