diff --git a/shepherd/core.py b/shepherd/core.py index cf353d5..ecf4585 100644 --- a/shepherd/core.py +++ b/shepherd/core.py @@ -4,10 +4,12 @@ Core shepherd module, tying together main service functionality. import os +import sys from pathlib import Path import glob from datetime import datetime import logging +import chromalog import pkg_resources import click @@ -18,49 +20,45 @@ from . import plugin from . import control +chromalog.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) +log = logging.getLogger("shepherd-agent") + + @click.group(invoke_without_command=True) -#help="Path to default config TOML file" -@click.argument('default_config_path', required=False, type=click.Path()) -@click.option('-l', '--local-config', 'only_local_layers', is_flag=True, - help="Only use the local config layers (default and custom)") -@click.option('-d', '--default-config', 'only_default_layer', is_flag=True, - help="Only use the default config layer (ignore" - "custom and control layers)") +@click.option('-c', '--config', 'default_config_path', type=click.Path(), + help="Shepherd config TOML file to be used as default config layer." + " Overrides default './shepherd*.toml' search") +@click.option('-l', '--local', 'local_operation', is_flag=True, + help="Only use the local config layers (default and custom), and disable all" + " Shepherd Control remote features") +@click.option('-d', '--default-config-only', 'only_default_layer', is_flag=True, + help="Ignore the custom config layer (still uses the Control config above that)") @click.pass_context -def cli(ctx, default_config_path, only_local_layers, only_default_layer): +def cli(ctx, default_config_path, local_operation, only_default_layer): """ - Core service. DEFAULT_CONFIG_PATH must point to a valid Shepherd config TOML file. If not - provided, the first filename in the current working directory beginning with "shepherd" and + Core service. If default config file is not provided with '-c' option, the first filename + in the current working directory beginning with "shepherd" and ending with ".toml" will be used. """ version_text = pkg_resources.get_distribution("shepherd") - logging.info(F"Initialising Shepherd Agent {version_text}") - # argparser = argparse.ArgumentParser(description="Keep track of a mob " - # "of roaming Pis") - # argparser.add_argument("configfile", nargs='?', metavar="configfile", - # help="Path to configfile", default="shepherd.toml") - # argparser.add_argument( - # '-e', '--noedit', help="Disable the editable config temporarily", action="store_true", default=False) - # argparser.add_argument("-t", "--test", help="Test and interface function of the from 'plugin:function'", - # default=None) - - #args = argparser.parse_args() - - if default_config_path is None: + log.info(F"Initialising Shepherd Agent {version_text}") + + if not default_config_path: default_config_path = sorted(glob.glob("./shepherd*.toml"))[:1] if default_config_path: default_config_path = default_config_path[0] - logging.info(F"No default config file provided, using {default_config_path}") + log.info(F"No default config file provided, using {default_config_path}") else: - raise Exception("No default config file provided, and no 'shepherd*.toml' could be" - " found in the current directory") + log.error("No default config file provided, and no 'shepherd*.toml' could be" + " found in the current directory") + sys.exit(1) layers_disabled = [] - if only_local_layers: + if local_operation: layers_disabled.append("control") + log.info("Running in local only mode") if only_default_layer: - layers_disabled.append("control") layers_disabled.append("custom") confman = ConfigManager() @@ -88,6 +86,11 @@ def cli(ctx, default_config_path, only_local_layers, only_default_layer): pass +@cli.command() +def test(): + print("test!") + + def compile_config_and_get_plugins(confman, default_config_path, layers_disabled): """ Run through the process of assembling the various config layers, falling back to working @@ -104,10 +107,13 @@ def compile_config_and_get_plugins(confman, default_config_path, layers_disabled default_config_path = Path(default_config_path).expanduser() try: plugin_classes = load_config_layer_and_plugins(confman, default_config_path) - logging.info(F"Loaded default config layer from {default_config_path}") - except Exception: - logging.error(F"Failed to load default config from {default_config_path}") - raise + log.info(F"Loaded default config layer from {default_config_path}") + except Exception as e: + if isinstance(e, InvalidConfigError): + log.error(F"Failed to load default config from {default_config_path}. {e.args[0]}") + else: + log.error(F"Failed to load default config from {default_config_path}", exc_info=True) + sys.exit(1) # Resolve and freeze local install paths that shouldn't be changed from default config core_conf = confman.get_config_bundle("shepherd") @@ -126,14 +132,19 @@ def compile_config_and_get_plugins(confman, default_config_path, layers_disabled if "custom" not in layers_disabled: try: plugin_classes = load_config_layer_and_plugins(confman, custom_config_path) - logging.info(F"Loaded custom config layer from {custom_config_path}") + log.info(F"Loaded custom config layer from {custom_config_path}") except Exception as e: - logging.error( - F"Failed to load custom config layer from {custom_config_path}. Falling back" - " to default config.", exc_info=e) + if isinstance(e, InvalidConfigError): + log.error(F"Failed to load custom config layer from {custom_config_path}." + F" {e.args[0]}") + else: + log.error(F"Failed to load custom config layer from {custom_config_path}.", + exc_info=True) + log.warning("Falling back to default config.") confman.fallback() + else: - logging.info("Custom config layer disabled") + log.info("Custom config layer disabled") # Freeze Shepherd Control related config. core_conf = confman.get_config_bundle("shepherd") @@ -151,18 +162,22 @@ def compile_config_and_get_plugins(confman, default_config_path, layers_disabled control_config = control.get_config(core_conf["root_dir"]) try: plugin_classes = load_config_layer_and_plugins(confman, control_config) - logging.info(F"Loaded cached Shepherd Control config layer") + log.info(F"Loaded cached Shepherd Control config layer") except Exception as e: - logging.error( - F"Failed to load cached Shepherd Control config layer. Falling back" - " to local config.", exc_info=e) + if isinstance(e, InvalidConfigError): + log.error(F"Failed to load cached Shepherd Control config layer." + F" {e.args[0]}") + else: + log.error(F"Failed to load cached Shepherd Control config layer.", + exc_info=True) + log.warning("Falling back to local config.") confman.fallback() except Exception: - logging.warning("No cached Shepherd Control config layer available.") + log.warning("No cached Shepherd Control config layer available.") else: - logging.info("Shepherd Control config layer disabled") + log.info("Shepherd Control config layer disabled") - logging.debug("Compiled config: %s", confman.root_config) + log.debug("Compiled config: %s", confman.root_config) confman.dump_to_file(core_conf["generated_config_path"]) return plugin_classes