2018-07-27 21:08:26 -07:00
|
|
|
"""
|
|
|
|
|
Commandline interface for setting config items in config.yaml.
|
|
|
|
|
|
|
|
|
|
Used as:
|
|
|
|
|
|
|
|
|
|
tljh-config set firstlevel.second_level something
|
|
|
|
|
|
|
|
|
|
tljh-config show
|
|
|
|
|
|
|
|
|
|
tljh-config show firstlevel
|
|
|
|
|
|
|
|
|
|
tljh-config show firstlevel.second_level
|
|
|
|
|
"""
|
2018-07-31 12:02:47 +02:00
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
import argparse
|
2018-07-31 12:02:47 +02:00
|
|
|
import os
|
2018-07-31 13:24:37 +02:00
|
|
|
import re
|
2018-07-27 21:08:26 -07:00
|
|
|
import sys
|
2019-02-13 14:10:28 +02:00
|
|
|
import time
|
2023-05-15 08:51:35 +00:00
|
|
|
from collections.abc import Mapping, Sequence
|
2024-09-05 11:58:51 +02:00
|
|
|
from contextlib import contextmanager
|
2023-05-15 08:51:35 +00:00
|
|
|
from copy import deepcopy
|
2019-02-13 14:10:28 +02:00
|
|
|
|
|
|
|
|
import requests
|
2024-09-05 11:58:51 +02:00
|
|
|
from filelock import FileLock, Timeout
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2018-11-01 11:34:16 +01:00
|
|
|
from .yaml import yaml
|
2018-07-27 21:08:26 -07:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
INSTALL_PREFIX = os.environ.get("TLJH_INSTALL_PREFIX", "/opt/tljh")
|
|
|
|
|
HUB_ENV_PREFIX = os.path.join(INSTALL_PREFIX, "hub")
|
|
|
|
|
USER_ENV_PREFIX = os.path.join(INSTALL_PREFIX, "user")
|
|
|
|
|
STATE_DIR = os.path.join(INSTALL_PREFIX, "state")
|
|
|
|
|
CONFIG_DIR = os.path.join(INSTALL_PREFIX, "config")
|
|
|
|
|
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.yaml")
|
2018-08-28 14:00:43 +02:00
|
|
|
|
2018-07-31 12:02:47 +02:00
|
|
|
|
2024-09-05 11:58:51 +02:00
|
|
|
@contextmanager
|
|
|
|
|
def config_file_lock(config_path, timeout=1):
|
|
|
|
|
"""Context manager to acquire the config file lock"""
|
|
|
|
|
lock_file = f"{config_path}.lock"
|
|
|
|
|
try:
|
|
|
|
|
with FileLock(lock_file).acquire(timeout=timeout):
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
except Timeout:
|
|
|
|
|
print(
|
|
|
|
|
f"Another instance of tljh-config holds the lock {lock_file}.",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
2018-07-27 21:08:26 -07:00
|
|
|
def set_item_in_config(config, property_path, value):
|
|
|
|
|
"""
|
|
|
|
|
Set key at property_path to value in config & return new config.
|
|
|
|
|
|
|
|
|
|
config is not mutated.
|
|
|
|
|
|
2018-07-28 11:05:29 -07:00
|
|
|
property_path is a series of dot separated values. Any part of the path
|
2018-07-31 12:02:47 +02:00
|
|
|
that does not exist is created.
|
2018-07-27 21:08:26 -07:00
|
|
|
"""
|
2021-11-03 23:55:34 +01:00
|
|
|
path_components = property_path.split(".")
|
2018-07-27 21:08:26 -07:00
|
|
|
|
|
|
|
|
# Mutate a copy of the config, not config itself
|
|
|
|
|
cur_part = config_copy = deepcopy(config)
|
2018-07-28 11:05:29 -07:00
|
|
|
for i, cur_path in enumerate(path_components):
|
2018-07-27 21:08:26 -07:00
|
|
|
cur_path = path_components[i]
|
|
|
|
|
if i == len(path_components) - 1:
|
|
|
|
|
# Final component
|
|
|
|
|
cur_part[cur_path] = value
|
|
|
|
|
else:
|
|
|
|
|
# If we are asked to create new non-leaf nodes, we will always make them dicts
|
|
|
|
|
# This means setting is *destructive* - will replace whatever is down there!
|
2018-08-10 17:26:35 -07:00
|
|
|
if cur_path not in cur_part or not _is_dict(cur_part[cur_path]):
|
2018-07-27 21:08:26 -07:00
|
|
|
cur_part[cur_path] = {}
|
|
|
|
|
cur_part = cur_part[cur_path]
|
|
|
|
|
|
|
|
|
|
return config_copy
|
|
|
|
|
|
2020-10-20 00:14:15 +02:00
|
|
|
|
2019-05-28 16:13:42 +03:00
|
|
|
def unset_item_from_config(config, property_path):
|
|
|
|
|
"""
|
|
|
|
|
Unset key at property_path in config & return new config.
|
|
|
|
|
|
|
|
|
|
config is not mutated.
|
|
|
|
|
|
|
|
|
|
property_path is a series of dot separated values.
|
|
|
|
|
"""
|
2021-11-03 23:55:34 +01:00
|
|
|
path_components = property_path.split(".")
|
2019-05-28 16:13:42 +03:00
|
|
|
|
|
|
|
|
# Mutate a copy of the config, not config itself
|
|
|
|
|
cur_part = config_copy = deepcopy(config)
|
|
|
|
|
|
|
|
|
|
def remove_empty_configs(configuration, path):
|
|
|
|
|
"""
|
|
|
|
|
Delete the keys that hold an empty dict.
|
|
|
|
|
|
|
|
|
|
This might happen when we delete a config property
|
|
|
|
|
that has no siblings from a multi-level config.
|
|
|
|
|
"""
|
|
|
|
|
if not path:
|
|
|
|
|
return configuration
|
|
|
|
|
conf_iter = configuration
|
|
|
|
|
for cur_path in path:
|
|
|
|
|
if conf_iter[cur_path] == {}:
|
|
|
|
|
del conf_iter[cur_path]
|
|
|
|
|
remove_empty_configs(configuration, path[:-1])
|
|
|
|
|
else:
|
|
|
|
|
conf_iter = conf_iter[cur_path]
|
|
|
|
|
|
|
|
|
|
for i, cur_path in enumerate(path_components):
|
|
|
|
|
if i == len(path_components) - 1:
|
|
|
|
|
if cur_path not in cur_part:
|
2021-11-03 23:55:34 +01:00
|
|
|
raise ValueError(f"{property_path} does not exist in config!")
|
2019-05-28 16:13:42 +03:00
|
|
|
del cur_part[cur_path]
|
|
|
|
|
remove_empty_configs(config_copy, path_components[:-1])
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
if cur_path not in cur_part:
|
2021-11-03 23:55:34 +01:00
|
|
|
raise ValueError(f"{property_path} does not exist in config!")
|
2019-05-28 16:13:42 +03:00
|
|
|
cur_part = cur_part[cur_path]
|
|
|
|
|
|
|
|
|
|
return config_copy
|
|
|
|
|
|
2020-10-20 00:14:15 +02:00
|
|
|
|
2018-07-28 11:05:29 -07:00
|
|
|
def add_item_to_config(config, property_path, value):
|
|
|
|
|
"""
|
|
|
|
|
Add an item to a list in config.
|
|
|
|
|
"""
|
2021-11-03 23:55:34 +01:00
|
|
|
path_components = property_path.split(".")
|
2018-07-28 11:05:29 -07:00
|
|
|
|
|
|
|
|
# Mutate a copy of the config, not config itself
|
|
|
|
|
cur_part = config_copy = deepcopy(config)
|
|
|
|
|
for i, cur_path in enumerate(path_components):
|
|
|
|
|
if i == len(path_components) - 1:
|
|
|
|
|
# Final component, it must be a list and we append to it
|
2018-08-10 17:26:35 -07:00
|
|
|
if cur_path not in cur_part or not _is_list(cur_part[cur_path]):
|
2018-07-28 11:05:29 -07:00
|
|
|
cur_part[cur_path] = []
|
|
|
|
|
cur_part = cur_part[cur_path]
|
|
|
|
|
|
|
|
|
|
cur_part.append(value)
|
|
|
|
|
else:
|
|
|
|
|
# If we are asked to create new non-leaf nodes, we will always make them dicts
|
|
|
|
|
# This means setting is *destructive* - will replace whatever is down there!
|
2018-08-10 17:26:35 -07:00
|
|
|
if cur_path not in cur_part or not _is_dict(cur_part[cur_path]):
|
2018-07-28 11:05:29 -07:00
|
|
|
cur_part[cur_path] = {}
|
|
|
|
|
cur_part = cur_part[cur_path]
|
|
|
|
|
|
|
|
|
|
return config_copy
|
|
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2018-07-28 11:57:11 -07:00
|
|
|
def remove_item_from_config(config, property_path, value):
|
|
|
|
|
"""
|
2019-01-22 16:24:38 +02:00
|
|
|
Remove an item from a list in config.
|
2018-07-28 11:57:11 -07:00
|
|
|
"""
|
2021-11-03 23:55:34 +01:00
|
|
|
path_components = property_path.split(".")
|
2018-07-28 11:57:11 -07:00
|
|
|
|
|
|
|
|
# Mutate a copy of the config, not config itself
|
|
|
|
|
cur_part = config_copy = deepcopy(config)
|
|
|
|
|
for i, cur_path in enumerate(path_components):
|
|
|
|
|
if i == len(path_components) - 1:
|
2019-05-28 16:13:42 +03:00
|
|
|
# Final component, it must be a list and we delete from it
|
2018-08-10 17:26:35 -07:00
|
|
|
if cur_path not in cur_part or not _is_list(cur_part[cur_path]):
|
2021-11-03 23:55:34 +01:00
|
|
|
raise ValueError(f"{property_path} is not a list")
|
2018-07-28 11:57:11 -07:00
|
|
|
cur_part = cur_part[cur_path]
|
|
|
|
|
cur_part.remove(value)
|
|
|
|
|
else:
|
2018-08-10 17:26:35 -07:00
|
|
|
if cur_path not in cur_part or not _is_dict(cur_part[cur_path]):
|
2021-11-03 23:55:34 +01:00
|
|
|
raise ValueError(f"{property_path} does not exist in config!")
|
2018-07-28 11:57:11 -07:00
|
|
|
cur_part = cur_part[cur_path]
|
|
|
|
|
|
|
|
|
|
return config_copy
|
|
|
|
|
|
2018-07-28 11:05:29 -07:00
|
|
|
|
2024-03-20 14:36:49 -04:00
|
|
|
def validate_config(config, validate):
|
2024-02-05 14:52:31 -05:00
|
|
|
"""
|
|
|
|
|
Validate changes to the config with tljh-config against the schema
|
|
|
|
|
"""
|
2024-02-02 23:07:38 -05:00
|
|
|
import jsonschema
|
2024-02-03 13:08:27 -05:00
|
|
|
|
|
|
|
|
from .config_schema import config_schema
|
2024-02-02 22:52:16 -05:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
jsonschema.validate(instance=config, schema=config_schema)
|
|
|
|
|
except jsonschema.exceptions.ValidationError as e:
|
2024-03-20 14:36:49 -04:00
|
|
|
if validate:
|
|
|
|
|
print(
|
|
|
|
|
f"Config validation error: {e.message}.\n"
|
|
|
|
|
"You can still apply this change without validation by re-running your command with the --no-validate flag.\n"
|
2024-09-05 11:58:51 +02:00
|
|
|
"If you think this validation error is incorrect, please report it to https://github.com/jupyterhub/the-littlest-jupyterhub/issues.",
|
|
|
|
|
file=sys.stderr,
|
2024-03-20 14:36:49 -04:00
|
|
|
)
|
2024-09-05 11:58:51 +02:00
|
|
|
sys.exit(1)
|
2024-02-02 21:51:23 -05:00
|
|
|
|
|
|
|
|
|
2018-07-27 21:08:26 -07:00
|
|
|
def show_config(config_path):
|
|
|
|
|
"""
|
|
|
|
|
Pretty print config from given config_path
|
|
|
|
|
"""
|
2024-04-06 10:49:07 -04:00
|
|
|
config = get_current_config(config_path)
|
2018-07-27 21:08:26 -07:00
|
|
|
yaml.dump(config, sys.stdout)
|
|
|
|
|
|
|
|
|
|
|
2024-04-03 16:03:05 -04:00
|
|
|
def set_config_value(config_path, key_path, value, validate=True):
|
2018-07-27 21:08:26 -07:00
|
|
|
"""
|
|
|
|
|
Set key at key_path in config_path to value
|
|
|
|
|
"""
|
2024-09-05 11:58:51 +02:00
|
|
|
with config_file_lock(config_path):
|
|
|
|
|
config = get_current_config(config_path)
|
|
|
|
|
config = set_item_in_config(config, key_path, value)
|
|
|
|
|
validate_config(config, validate)
|
2024-04-06 10:49:07 -04:00
|
|
|
|
2024-09-05 11:58:51 +02:00
|
|
|
with open(config_path, "w") as f:
|
|
|
|
|
yaml.dump(config, f)
|
2018-07-27 21:08:26 -07:00
|
|
|
|
|
|
|
|
|
2024-04-03 16:03:14 -04:00
|
|
|
def unset_config_value(config_path, key_path, validate=True):
|
2019-05-28 16:13:42 +03:00
|
|
|
"""
|
|
|
|
|
Unset key at key_path in config_path
|
|
|
|
|
"""
|
2024-09-05 11:58:51 +02:00
|
|
|
with config_file_lock(config_path):
|
|
|
|
|
config = get_current_config(config_path)
|
|
|
|
|
config = unset_item_from_config(config, key_path)
|
|
|
|
|
validate_config(config, validate)
|
2019-05-28 16:13:42 +03:00
|
|
|
|
2024-09-05 11:58:51 +02:00
|
|
|
with open(config_path, "w") as f:
|
|
|
|
|
yaml.dump(config, f)
|
2019-05-28 16:13:42 +03:00
|
|
|
|
|
|
|
|
|
2024-04-03 16:02:55 -04:00
|
|
|
def add_config_value(config_path, key_path, value, validate=True):
|
2018-07-28 11:05:29 -07:00
|
|
|
"""
|
|
|
|
|
Add value to list at key_path
|
|
|
|
|
"""
|
2024-09-05 11:58:51 +02:00
|
|
|
with config_file_lock(config_path):
|
|
|
|
|
config = get_current_config(config_path)
|
|
|
|
|
config = add_item_to_config(config, key_path, value)
|
|
|
|
|
validate_config(config, validate)
|
2024-04-06 10:49:07 -04:00
|
|
|
|
2024-09-05 11:58:51 +02:00
|
|
|
with open(config_path, "w") as f:
|
|
|
|
|
yaml.dump(config, f)
|
2018-07-27 21:08:26 -07:00
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2024-04-03 16:05:16 -04:00
|
|
|
def remove_config_value(config_path, key_path, value, validate=True):
|
2018-07-31 13:24:37 +02:00
|
|
|
"""
|
|
|
|
|
Remove value from list at key_path
|
|
|
|
|
"""
|
2024-09-05 11:58:51 +02:00
|
|
|
with config_file_lock(config_path):
|
|
|
|
|
config = get_current_config(config_path)
|
|
|
|
|
config = remove_item_from_config(config, key_path, value)
|
|
|
|
|
validate_config(config, validate)
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2024-09-05 11:58:51 +02:00
|
|
|
with open(config_path, "w") as f:
|
|
|
|
|
yaml.dump(config, f)
|
2024-04-06 10:49:07 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_current_config(config_path):
|
|
|
|
|
"""
|
|
|
|
|
Retrieve the current config at config_path
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
with open(config_path) as f:
|
|
|
|
|
return yaml.load(f)
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
return {}
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2020-10-20 00:14:15 +02:00
|
|
|
|
2019-02-11 09:24:16 +02:00
|
|
|
def check_hub_ready():
|
2024-04-06 10:49:07 -04:00
|
|
|
"""
|
|
|
|
|
Checks that hub is running.
|
|
|
|
|
"""
|
2020-07-21 14:36:53 +09:00
|
|
|
from .configurer import load_config
|
2020-10-19 19:48:23 +02:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
base_url = load_config()["base_url"]
|
|
|
|
|
base_url = base_url[:-1] if base_url[-1] == "/" else base_url
|
2023-05-24 11:42:27 +02:00
|
|
|
http_address = load_config()["http"]["address"]
|
2021-11-03 23:55:34 +01:00
|
|
|
http_port = load_config()["http"]["port"]
|
2023-05-24 11:42:27 +02:00
|
|
|
# The default config is an empty address, so it binds on all interfaces.
|
|
|
|
|
# Test the connectivity on the local address.
|
2023-05-25 12:56:21 +00:00
|
|
|
if http_address == "":
|
|
|
|
|
http_address = "127.0.0.1"
|
2019-02-11 09:24:16 +02:00
|
|
|
try:
|
2021-11-01 09:42:45 +01:00
|
|
|
r = requests.get(
|
2023-05-24 11:42:27 +02:00
|
|
|
"http://%s:%d%s/hub/api" % (http_address, http_port, base_url), verify=False
|
2021-11-01 09:42:45 +01:00
|
|
|
)
|
2023-05-15 10:53:53 +02:00
|
|
|
if r.status_code != 200:
|
|
|
|
|
print(f"Hub not ready: (HTTP status {r.status_code})")
|
2019-02-11 09:24:16 +02:00
|
|
|
return r.status_code == 200
|
2023-05-15 10:53:53 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Hub not ready: {e}")
|
2019-02-11 09:24:16 +02:00
|
|
|
return False
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2020-10-20 00:14:15 +02:00
|
|
|
|
2018-07-27 21:08:26 -07:00
|
|
|
def reload_component(component):
|
|
|
|
|
"""
|
|
|
|
|
Reload a TLJH component.
|
|
|
|
|
|
|
|
|
|
component can be 'hub' or 'proxy'.
|
|
|
|
|
"""
|
2018-07-31 12:05:47 +02:00
|
|
|
# import here to avoid circular imports
|
|
|
|
|
from tljh import systemd, traefik
|
2019-02-11 09:24:16 +02:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
if component == "hub":
|
|
|
|
|
systemd.restart_service("jupyterhub")
|
2019-01-22 16:24:38 +02:00
|
|
|
# Ensure hub is back up
|
2021-11-03 23:55:34 +01:00
|
|
|
while not systemd.check_service_active("jupyterhub"):
|
2019-02-11 09:24:16 +02:00
|
|
|
time.sleep(1)
|
|
|
|
|
while not check_hub_ready():
|
|
|
|
|
time.sleep(1)
|
2021-11-03 23:55:34 +01:00
|
|
|
print("Hub reload with new configuration complete")
|
|
|
|
|
elif component == "proxy":
|
2018-07-31 12:02:47 +02:00
|
|
|
traefik.ensure_traefik_config(STATE_DIR)
|
2021-11-03 23:55:34 +01:00
|
|
|
systemd.restart_service("traefik")
|
|
|
|
|
while not systemd.check_service_active("traefik"):
|
2019-02-11 09:24:16 +02:00
|
|
|
time.sleep(1)
|
2021-11-03 23:55:34 +01:00
|
|
|
print("Proxy reload with new configuration complete")
|
2018-07-27 21:08:26 -07:00
|
|
|
|
|
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
def parse_value(value_str):
|
|
|
|
|
"""Parse a value string"""
|
|
|
|
|
if value_str is None:
|
|
|
|
|
return value_str
|
2021-11-03 23:55:34 +01:00
|
|
|
if re.match(r"^\d+$", value_str):
|
2018-07-31 13:24:37 +02:00
|
|
|
return int(value_str)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif re.match(r"^\d+\.\d*$", value_str):
|
2018-07-31 13:24:37 +02:00
|
|
|
return float(value_str)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif value_str.lower() == "true":
|
2018-07-31 13:24:37 +02:00
|
|
|
return True
|
2024-02-03 13:27:58 -05:00
|
|
|
elif value_str.lower() == "false":
|
2018-07-31 13:24:37 +02:00
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
# it's a string
|
|
|
|
|
return value_str
|
|
|
|
|
|
|
|
|
|
|
2018-08-10 17:26:35 -07:00
|
|
|
def _is_dict(item):
|
2018-11-01 11:34:16 +01:00
|
|
|
return isinstance(item, Mapping)
|
2018-08-10 17:26:35 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _is_list(item):
|
2018-11-01 11:34:16 +01:00
|
|
|
return isinstance(item, Sequence)
|
2018-08-10 17:26:35 -07:00
|
|
|
|
|
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
def main(argv=None):
|
2018-10-23 23:45:33 +03:00
|
|
|
if os.geteuid() != 0:
|
2018-11-13 12:28:41 -08:00
|
|
|
print("tljh-config needs root privileges to run", file=sys.stderr)
|
2021-11-01 09:42:45 +01:00
|
|
|
print(
|
|
|
|
|
"Try using sudo before the tljh-config command you wanted to run",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
2018-11-13 12:28:41 -08:00
|
|
|
sys.exit(1)
|
2018-10-23 23:45:33 +03:00
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
if argv is None:
|
|
|
|
|
argv = sys.argv[1:]
|
|
|
|
|
|
2018-08-31 12:00:42 +02:00
|
|
|
from .log import init_logging
|
2021-11-01 09:42:45 +01:00
|
|
|
|
2018-10-21 23:55:47 +03:00
|
|
|
try:
|
|
|
|
|
init_logging()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(str(e))
|
|
|
|
|
print("Perhaps you didn't use `sudo -E`?")
|
2018-08-31 12:00:42 +02:00
|
|
|
|
2018-07-27 21:08:26 -07:00
|
|
|
argparser = argparse.ArgumentParser()
|
|
|
|
|
argparser.add_argument(
|
2021-11-03 23:55:34 +01:00
|
|
|
"--config-path", default=CONFIG_FILE, help="Path to TLJH config.yaml file"
|
2018-07-27 21:08:26 -07:00
|
|
|
)
|
2024-04-03 13:30:32 -04:00
|
|
|
|
|
|
|
|
argparser.add_argument(
|
|
|
|
|
"--validate", action="store_true", help="Validate the TLJH config"
|
|
|
|
|
)
|
2024-03-20 14:36:49 -04:00
|
|
|
argparser.add_argument(
|
2024-04-03 13:30:32 -04:00
|
|
|
"--no-validate",
|
|
|
|
|
dest="validate",
|
|
|
|
|
action="store_false",
|
|
|
|
|
help="Do not validate the TLJH config",
|
2024-03-20 14:36:49 -04:00
|
|
|
)
|
2024-04-03 13:30:32 -04:00
|
|
|
argparser.set_defaults(validate=True)
|
2024-03-20 14:36:49 -04:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
subparsers = argparser.add_subparsers(dest="action")
|
2018-07-27 21:08:26 -07:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
show_parser = subparsers.add_parser("show", help="Show current configuration")
|
2018-07-27 21:08:26 -07:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
unset_parser = subparsers.add_parser("unset", help="Unset a configuration property")
|
2019-05-28 16:13:42 +03:00
|
|
|
unset_parser.add_argument(
|
2021-11-03 23:55:34 +01:00
|
|
|
"key_path", help="Dot separated path to configuration key to unset"
|
2019-05-28 16:13:42 +03:00
|
|
|
)
|
|
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
set_parser = subparsers.add_parser("set", help="Set a configuration property")
|
2018-07-27 21:08:26 -07:00
|
|
|
set_parser.add_argument(
|
2021-11-03 23:55:34 +01:00
|
|
|
"key_path", help="Dot separated path to configuration key to set"
|
2018-07-27 21:08:26 -07:00
|
|
|
)
|
2021-11-03 23:55:34 +01:00
|
|
|
set_parser.add_argument("value", help="Value to set the configuration key to")
|
2018-07-27 21:08:26 -07:00
|
|
|
|
2018-07-28 11:05:29 -07:00
|
|
|
add_item_parser = subparsers.add_parser(
|
2021-11-03 23:55:34 +01:00
|
|
|
"add-item", help="Add a value to a list for a configuration property"
|
2018-07-28 11:05:29 -07:00
|
|
|
)
|
|
|
|
|
add_item_parser.add_argument(
|
2021-11-03 23:55:34 +01:00
|
|
|
"key_path", help="Dot separated path to configuration key to add value to"
|
2018-07-28 11:57:11 -07:00
|
|
|
)
|
2021-11-03 23:55:34 +01:00
|
|
|
add_item_parser.add_argument("value", help="Value to add to the configuration key")
|
2018-07-28 11:57:11 -07:00
|
|
|
|
|
|
|
|
remove_item_parser = subparsers.add_parser(
|
2021-11-03 23:55:34 +01:00
|
|
|
"remove-item", help="Remove a value from a list for a configuration property"
|
2018-07-28 11:57:11 -07:00
|
|
|
)
|
|
|
|
|
remove_item_parser.add_argument(
|
2021-11-03 23:55:34 +01:00
|
|
|
"key_path", help="Dot separated path to configuration key to remove value from"
|
2018-07-28 11:05:29 -07:00
|
|
|
)
|
2021-11-03 23:55:34 +01:00
|
|
|
remove_item_parser.add_argument("value", help="Value to remove from key_path")
|
2018-07-28 11:05:29 -07:00
|
|
|
|
2018-07-27 21:08:26 -07:00
|
|
|
reload_parser = subparsers.add_parser(
|
2021-11-03 23:55:34 +01:00
|
|
|
"reload", help="Reload a component to apply configuration change"
|
2018-07-27 21:08:26 -07:00
|
|
|
)
|
|
|
|
|
reload_parser.add_argument(
|
2021-11-03 23:55:34 +01:00
|
|
|
"component",
|
|
|
|
|
choices=("hub", "proxy"),
|
|
|
|
|
help="Which component to reload",
|
|
|
|
|
default="hub",
|
|
|
|
|
nargs="?",
|
2018-07-27 21:08:26 -07:00
|
|
|
)
|
|
|
|
|
|
2018-07-31 13:24:37 +02:00
|
|
|
args = argparser.parse_args(argv)
|
2018-07-27 21:08:26 -07:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
if args.action == "show":
|
2018-10-23 23:45:33 +03:00
|
|
|
show_config(args.config_path)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif args.action == "set":
|
2024-03-20 14:36:49 -04:00
|
|
|
set_config_value(
|
|
|
|
|
args.config_path, args.key_path, parse_value(args.value), args.validate
|
|
|
|
|
)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif args.action == "unset":
|
2024-03-20 14:36:49 -04:00
|
|
|
unset_config_value(args.config_path, args.key_path, args.validate)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif args.action == "add-item":
|
2024-03-20 14:36:49 -04:00
|
|
|
add_config_value(
|
|
|
|
|
args.config_path, args.key_path, parse_value(args.value), args.validate
|
|
|
|
|
)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif args.action == "remove-item":
|
2024-03-20 14:36:49 -04:00
|
|
|
remove_config_value(
|
|
|
|
|
args.config_path, args.key_path, parse_value(args.value), args.validate
|
|
|
|
|
)
|
2021-11-03 23:55:34 +01:00
|
|
|
elif args.action == "reload":
|
2018-10-23 23:45:33 +03:00
|
|
|
reload_component(args.component)
|
|
|
|
|
else:
|
|
|
|
|
argparser.print_help()
|
2018-07-31 13:24:37 +02:00
|
|
|
|
2018-07-30 15:00:59 +02:00
|
|
|
|
2021-11-03 23:55:34 +01:00
|
|
|
if __name__ == "__main__":
|
2018-07-30 15:00:59 +02:00
|
|
|
main()
|