From 2307e83e66bd2f71cc0e0ab2da1c5f2b5dbdb698 Mon Sep 17 00:00:00 2001 From: Christian Busch Date: Fri, 4 Nov 2022 00:27:08 +0100 Subject: [PATCH] refactor: move cli command logic - remove ifelse in __main__ - rename api folder to cli - prepare decoupling of cli from getdeck lib --- getdeck/__main__.py | 144 +++++++++++-------------------- getdeck/{api => cli}/__init__.py | 0 getdeck/cli/console.py | 17 ++++ getdeck/{api => cli}/get.py | 24 +++++- getdeck/{api => cli}/hosts.py | 10 ++- getdeck/{api => cli}/list.py | 8 +- getdeck/{api => cli}/remove.py | 9 +- getdeck/{api => cli}/stop.py | 6 +- getdeck/cli/telemetry.py | 22 +++++ getdeck/{api => cli}/utils.py | 0 getdeck/cli/version.py | 10 +++ getdeck/configuration.py | 6 +- getdeck/fetch/fetch.py | 8 +- getdeck/k8s.py | 11 --- 14 files changed, 156 insertions(+), 119 deletions(-) rename getdeck/{api => cli}/__init__.py (100%) create mode 100644 getdeck/cli/console.py rename getdeck/{api => cli}/get.py (92%) rename getdeck/{api => cli}/hosts.py (91%) rename getdeck/{api => cli}/list.py (68%) rename getdeck/{api => cli}/remove.py (91%) rename getdeck/{api => cli}/stop.py (82%) create mode 100644 getdeck/cli/telemetry.py rename getdeck/{api => cli}/utils.py (100%) create mode 100644 getdeck/cli/version.py diff --git a/getdeck/__main__.py b/getdeck/__main__.py index 793bc09..39be665 100755 --- a/getdeck/__main__.py +++ b/getdeck/__main__.py @@ -3,8 +3,6 @@ import logging import os import traceback -from getdeck.telemetry.telemetry import CliTelemetry - os.environ["PYOXIDIZER"] = "1" @@ -13,6 +11,16 @@ ARGUMENT_DECKFILE_HELP = "the deck.yaml location (as file, git or https)" +def check_positive(value): + try: + ivalue = int(value) + except ValueError: + raise argparse.ArgumentTypeError("timeout must be a positive integer value") + if ivalue <= 0: + raise argparse.ArgumentTypeError("timeout must be a positive integer value") + return ivalue + + parser = argparse.ArgumentParser( prog="deck", description="The Deck CLI. For more help please visit: https://getdeck.dev", @@ -20,19 +28,19 @@ action = parser.add_subparsers(dest="action", help="the action to be performed") parser.add_argument("-d", "--debug", action="store_true", help="add debug output") -# list all decks of the given deck.yaml +# list list_parser = action.add_parser("list") list_parser.add_argument( "Deckfile", help=ARGUMENT_DECKFILE_HELP, nargs="?", default="." ) -# rollout the cluster and install the deck from the given deck.yaml +# get get_parser = action.add_parser("get") get_parser.add_argument("--name", help="the Deck that you want to run", required=False) get_parser.add_argument( "-I", "--no-cluster", - help="do not set up the cluster, use current kubectl context", + help="Do not set up the cluster, use current kubectl context", action="store_true", required=False, ) @@ -44,17 +52,6 @@ required=False, ) - -def check_positive(value): - try: - ivalue = int(value) - except ValueError: - raise argparse.ArgumentTypeError("timeout must be a positive integer value") - if ivalue <= 0: - raise argparse.ArgumentTypeError("timeout must be a positive integer value") - return ivalue - - get_parser.add_argument( "-T", "--timeout", @@ -73,6 +70,7 @@ def check_positive(value): ) get_parser.add_argument("Deckfile", help=ARGUMENT_DECKFILE_HELP, nargs="?", default=".") +# remove remove_parser = action.add_parser("remove") remove_parser.add_argument( "--name", help="the Deck that you want to remove", required=False @@ -86,7 +84,7 @@ def check_positive(value): remove_parser.add_argument( "-I", "--no-cluster", - help="do not set up the cluster, use current kubectl context", + help="Do not set up the cluster, use current kubectl context", action="store_true", default=False, required=False, @@ -95,11 +93,12 @@ def check_positive(value): "Deckfile", help=ARGUMENT_DECKFILE_HELP, nargs="?", default="." ) +# stop stop_parser = action.add_parser("stop") stop_parser.add_argument( "-I", "--no-cluster", - help="do not set up the cluster, use current kubectl context", + help="Do not set up the cluster, use current kubectl context", action="store_true", required=False, ) @@ -107,103 +106,62 @@ def check_positive(value): "Deckfile", help=ARGUMENT_DECKFILE_HELP, nargs="?", default="." ) +# version version_parser = action.add_parser("version") - +# hosts hosts_parser = action.add_parser("hosts") hosts_parser.add_argument("host_action", help="list/write/remove") hosts_parser.add_argument( "Deckfile", help=ARGUMENT_DECKFILE_HELP, nargs="?", default="." ) hosts_parser.add_argument( - "--name", help="the Deck whose hosts will be considered", required=False + "--name", help="The Deck whose hosts will be considered", required=False ) +# telemetry telemetry_parser = action.add_parser("telemetry") telemetry_parser.add_argument("--off", help="Turn off telemetry", action="store_true") telemetry_parser.add_argument("--on", help="Turn on telemetry", action="store_true") -try: - telemetry = CliTelemetry() -except Exception: - telemetry = False - - -def telemetry_command(on, off): - if not telemetry: - logger.info("Telemetry in not working on your machine. No action taken.") - return - if off and not on: - telemetry.off() - elif on and not off: - telemetry.on() - else: - logger.info("Invalid flags. Please use either --off or --on.") - def main(): - from getdeck import configuration - from getdeck.api import ( - get_available_decks, - run_deck, - remove_cluster, - remove_deck, - stop_cluster, - ) - from getdeck.api.hosts import run_hosts + from getdeck.cli.console import console_setup + from getdeck.cli.get import get_command + from getdeck.cli.hosts import hosts_command + from getdeck.cli.list import list_command + from getdeck.cli.remove import remove_command + from getdeck.cli.stop import stop_command + from getdeck.cli.telemetry import telemetry_command + from getdeck.cli.version import version_command + + args = parser.parse_args() + debug = args.debug try: - args = parser.parse_args() - if args.debug: - logger.setLevel(logging.DEBUG) - else: - logger.setLevel(logging.INFO) - - logger.addHandler(configuration.console) - if args.action == "list": - decks = get_available_decks(args.Deckfile) - names = [deck.name for deck in decks] - logger.info(names) - elif args.action == "get": - if args.wait: - wait = True - timeout = int(args.timeout) - else: - wait = False - timeout = None - - run_deck( - args.Deckfile, - args.name, - ignore_cluster=args.no_cluster, - wait=wait, - timeout=timeout, - no_input=args.no_input, - ) - elif args.action == "remove": - if args.cluster: - remove_cluster(args.Deckfile, ignore_cluster=args.no_cluster) - else: - remove_deck(args.Deckfile, args.name, ignore_cluster=args.no_cluster) - elif args.action == "stop": - stop_cluster(args.Deckfile, ignore_cluster=args.no_cluster) - elif args.action == "version": - logger.info(f"Deck version: {configuration.__VERSION__}") - elif args.action == "hosts": - run_hosts( - args.Deckfile, - args.host_action, - deck_name=args.name, - ) - elif args.action == "telemetry": - telemetry_command(on=args.on, off=args.off) - else: + console_setup(debug=debug) + + command = { + "list": list_command, + "get": get_command, + "remove": remove_command, + "stop": stop_command, + "version": version_command, + "hosts": hosts_command, + "telemetry": telemetry_command, + }.get(args.action, None) + + # unknown / invalid command + if not command: parser.print_help() + exit(0) + + command(args=args) exit(0) except Exception as e: - if args.debug: + if debug: traceback.print_exc() - logger.fatal(f"There was an error running deck: {e}") + logger.critical(f"There was an error running deck: {e}") exit(1) diff --git a/getdeck/api/__init__.py b/getdeck/cli/__init__.py similarity index 100% rename from getdeck/api/__init__.py rename to getdeck/cli/__init__.py diff --git a/getdeck/cli/console.py b/getdeck/cli/console.py new file mode 100644 index 0000000..4a17426 --- /dev/null +++ b/getdeck/cli/console.py @@ -0,0 +1,17 @@ +import logging +import sys + +logger = logging.getLogger("deck") + + +def console_setup(debug: bool = False): + console = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter("[%(levelname)s] %(message)s") + console.setFormatter(formatter) + + if debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + logger.addHandler(console) diff --git a/getdeck/api/get.py b/getdeck/cli/get.py similarity index 92% rename from getdeck/api/get.py rename to getdeck/cli/get.py index a81b84c..72fc05e 100644 --- a/getdeck/api/get.py +++ b/getdeck/cli/get.py @@ -1,8 +1,8 @@ import logging from typing import Callable -from getdeck.api import stopwatch, remove -from getdeck.api.hosts import verify_all_hosts +from getdeck.cli import stopwatch, remove +from getdeck.cli.hosts import verify_all_hosts from getdeck.configuration import default_configuration from getdeck.k8s import create_namespace from getdeck.provider.types import ProviderType @@ -56,7 +56,7 @@ def run_deck( # noqa: C901 if progress_callback: progress_callback(20) - # change api for beiboot + # change kubeconfig for beiboot if k8s_provider.kubernetes_cluster_type == ProviderType.BEIBOOT: _old_kubeconfig = config.kubeconfig config.kubeconfig = k8s_provider.get_kubeconfig() @@ -169,3 +169,21 @@ def _wait_ready(config, generated_deck, timeout): raise RuntimeError( f"The Pods of this Deck did not become ready in time (timout was {timeout} s)." ) + + +def get_command(args): + if args.wait: + wait = True + timeout = int(args.timeout) + else: + wait = False + timeout = None + + run_deck( + args.Deckfile, + args.name, + ignore_cluster=args.no_cluster, + wait=wait, + timeout=timeout, + no_input=args.no_input, + ) diff --git a/getdeck/api/hosts.py b/getdeck/cli/hosts.py similarity index 91% rename from getdeck/api/hosts.py rename to getdeck/cli/hosts.py index f9fef01..83a6877 100644 --- a/getdeck/api/hosts.py +++ b/getdeck/cli/hosts.py @@ -3,7 +3,7 @@ from python_hosts import Hosts, HostsEntry -from getdeck.api import stopwatch +from getdeck.cli import stopwatch from getdeck.fetch.fetch import fetch_data logger = logging.getLogger("deck") @@ -63,3 +63,11 @@ def verify_host(host) -> bool: def verify_all_hosts(*hosts): return all(verify_host(host) for host in hosts) + + +def hosts_command(args): + run_hosts( + args.Deckfile, + args.host_action, + deck_name=args.name, + ) diff --git a/getdeck/api/list.py b/getdeck/cli/list.py similarity index 68% rename from getdeck/api/list.py rename to getdeck/cli/list.py index 44dc84e..8f2f443 100644 --- a/getdeck/api/list.py +++ b/getdeck/cli/list.py @@ -1,7 +1,7 @@ import logging from typing import List -from getdeck.api.utils import stopwatch +from getdeck.cli.utils import stopwatch from getdeck.fetch.fetch import fetch_data logger = logging.getLogger("deck") @@ -15,3 +15,9 @@ def get_available_decks(deckfile_location: str) -> List: logger.debug(available_decks) return available_decks + + +def list_command(args): + decks = get_available_decks(args.Deckfile) + names = [deck.name for deck in decks] + logger.info(names) diff --git a/getdeck/api/remove.py b/getdeck/cli/remove.py similarity index 91% rename from getdeck/api/remove.py rename to getdeck/cli/remove.py index 5216072..0835abb 100644 --- a/getdeck/api/remove.py +++ b/getdeck/cli/remove.py @@ -2,7 +2,7 @@ from typing import Callable from getdeck.configuration import default_configuration -from getdeck.api import stopwatch +from getdeck.cli import stopwatch from getdeck.fetch.fetch import fetch_data @@ -84,3 +84,10 @@ def remove_deck( del data_aux return True + + +def remove_command(args): + if args.cluster: + remove_cluster(args.Deckfile, ignore_cluster=args.no_cluster) + else: + remove_deck(args.Deckfile, args.name, ignore_cluster=args.no_cluster) diff --git a/getdeck/api/stop.py b/getdeck/cli/stop.py similarity index 82% rename from getdeck/api/stop.py rename to getdeck/cli/stop.py index 175675b..1636b45 100644 --- a/getdeck/api/stop.py +++ b/getdeck/cli/stop.py @@ -1,6 +1,6 @@ import logging -from getdeck.api import stopwatch +from getdeck.cli import stopwatch from getdeck.configuration import default_configuration from getdeck.fetch.fetch import fetch_data @@ -23,3 +23,7 @@ def stop_cluster( del data_aux k8s_provider.stop() + + +def stop_command(args): + stop_cluster(args.Deckfile, ignore_cluster=args.no_cluster) diff --git a/getdeck/cli/telemetry.py b/getdeck/cli/telemetry.py new file mode 100644 index 0000000..d412b3c --- /dev/null +++ b/getdeck/cli/telemetry.py @@ -0,0 +1,22 @@ +from getdeck.telemetry.telemetry import CliTelemetry +import logging + + +logger = logging.getLogger("deck") + +try: + telemetry = CliTelemetry() +except Exception: + telemetry = False + + +def telemetry_command(args): + if not telemetry: + logger.info("Telemetry is not working on your machine. No action taken.") + return + if args.off and not args.on: + telemetry.off() + elif args.on and not args.off: + telemetry.on() + else: + logger.info("Invalid flags. Please use either --off or --on.") diff --git a/getdeck/api/utils.py b/getdeck/cli/utils.py similarity index 100% rename from getdeck/api/utils.py rename to getdeck/cli/utils.py diff --git a/getdeck/cli/version.py b/getdeck/cli/version.py new file mode 100644 index 0000000..6b466f6 --- /dev/null +++ b/getdeck/cli/version.py @@ -0,0 +1,10 @@ +import logging + + +logger = logging.getLogger("deck") + + +def version_command(args): + from getdeck import configuration + + logger.info(f"Deck version: {configuration.__VERSION__}") diff --git a/getdeck/configuration.py b/getdeck/configuration.py index cb1ba3c..191b769 100644 --- a/getdeck/configuration.py +++ b/getdeck/configuration.py @@ -3,12 +3,8 @@ import logging -console = logging.StreamHandler(sys.stdout) -formatter = logging.Formatter("[%(levelname)s] %(message)s") -console.setFormatter(formatter) - logger = logging.getLogger("deck") -logger.addHandler(console) + __VERSION__ = "0.11.0" diff --git a/getdeck/fetch/fetch.py b/getdeck/fetch/fetch.py index 0586964..79cf368 100644 --- a/getdeck/fetch/fetch.py +++ b/getdeck/fetch/fetch.py @@ -90,7 +90,7 @@ def fetch_all_sources(deck: DeckfileDeck) -> List[SourceAux]: source_auxs = [] for source in deck.sources: ref = getattr(source, "ref", None) - logger.info(f"Fetching {source.__class__.__name__}: {ref or '-'}") + logger.info(f"Fetching {source.__class__.__name__}: {ref or 'no ref'}") source_aux = fetch_source(source=source, source_fetcher=source_fetcher) source_auxs.append(source_aux) @@ -109,7 +109,7 @@ def fetch_data( if display_location == ".": display_location = detect_deckfile() - logger.info(f"Reading Deckfile: {display_location}") + logger.info(f"Reading Deckfile: {display_location or '.'}") # fetch deck data_aux = DataAux() @@ -120,7 +120,9 @@ def fetch_data( if not os.path.isfile(file_detected): logger.debug(f"Absolute file location: {file_detected}") del data_aux - raise RuntimeError(f"Cannot identify Deckfile at location: {display_location}") + raise RuntimeError( + f"Cannot identify Deckfile at location: {display_location or '.'}" + ) deckfile = deckfile_selector.get(file_detected) data_aux.deckfile = deckfile diff --git a/getdeck/k8s.py b/getdeck/k8s.py index e3a40bd..1c45c01 100644 --- a/getdeck/k8s.py +++ b/getdeck/k8s.py @@ -16,17 +16,6 @@ def _call_and_log(config, api, verb, obj, namespace, **kwargs): ) -def _delete_and_create(config, api, obj, namespace, **kwargs): - logger.debug( - f"Kubernetes: replacing {k8s_describe_object(obj)} " - f"failed. Attempting deletion and recreation." - ) - k8s_call_api(config, api, "delete", obj, namespace, **kwargs) - if api: - logger.debug(f"Kubernetes: {k8s_describe_object(obj)} deleted") - _call_and_log(config, api, "create", obj, namespace, **kwargs) - - def create_namespace(config, name: str) -> None: from kubernetes.client.rest import ApiException from kubernetes.client import V1Namespace, V1ObjectMeta