diff --git a/README.md b/README.md index b5f5041..0aa9cf2 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ https://gitlab.nic.cz/knot/knot-dns-rest ## Usage ``` -usage: knotctl [-h] [--json | --no-json] {add,config,delete,list,update} ... +usage: knotctl [-h] [--json | --no-json] + {add,complete,config,delete,list,update} ... positional arguments: - {add,config,delete,list,update} + {add,complete,config,delete,list,update} options: -h, --help show this help message and exit @@ -27,6 +28,31 @@ options: -t TTL, --ttl TTL -z ZONE, --zone ZONE ``` +### COMPLETE +``` +usage: knotctl complete [-h] [-s SHELL] + +options: + -h, --help show this help message and exit + -s SHELL, --shell SHELL +``` + +For bash: add this to .bashrc +``` +eval "$(knotctl complete)" +``` +For fish, run: +``` +knotctl complete --shell fish > ~/.config/fish/completions/knotctl.fish +``` +For tcsh: add this to .cshrc +``` +eval "$(knotctl complete --shell tcsh)" +``` +For zsh: add this to .zshrc +``` +eval "$(autoload -U bashcompinit; bashcompinit; scripts/knotctl complete)" +``` ### CONFIG ``` usage: knotctl config [-h] [-b BASEURL] [-p PASSWORD] [-u USERNAME] diff --git a/scripts/knotctl b/scripts/knotctl index 02aa0ff..82ba424 100755 --- a/scripts/knotctl +++ b/scripts/knotctl @@ -3,8 +3,8 @@ import argparse import getpass import json +import os import sys -import urllib.parse from collections.abc import Sequence from os import environ, mkdir from os.path import isdir, isfile, join @@ -16,6 +16,7 @@ import yaml from requests.models import HTTPBasicAuth +# Helper functions def error(description: str, error: str) -> Sequence[dict]: response = [] reply = {} @@ -32,16 +33,6 @@ def get_config(config_filename: str): return yaml.safe_load(fh.read()) -def output(response: Sequence[dict], jsonout: bool = False): - try: - if jsonout: - print(json.dumps(response)) - else: - print(nested_out(response)) - except BrokenPipeError: - pass - - def nested_out(input, tabs="") -> str: string = "" if isinstance(input, str) or isinstance(input, int): @@ -56,13 +47,33 @@ def nested_out(input, tabs="") -> str: return string -# Define the parser for each command +def output(response: Sequence[dict], jsonout: bool = False): + try: + if jsonout: + print(json.dumps(response)) + else: + print(nested_out(response)) + except BrokenPipeError: + pass + + +# Define the runner for each command def run_add(url: str, jsonout: bool, headers: dict): print(url) response = requests.put(url, headers=headers) output(response.json(), jsonout) +def run_complete(shell: Union[None, str]): + if not shell or shell in ["bash", "zsh"]: + os.system("register-python-argcomplete knotctl") + elif shell == "fish": + os.system("register-python-argcomplete --shell fish knotctl") + elif shell == "tcsh": + os.system("register-python-argcomplete --shell tcsh knotctl", + shell=True) + + def run_config( config_filename: str, baseurl: Union[None, str] = None, @@ -150,32 +161,8 @@ def setup_url( return url +# Entry point to program def main() -> int: - config_basepath = join(environ["HOME"], ".knot") - config_filename = join(config_basepath, "config") - - if not isdir(config_basepath): - mkdir(config_basepath) - - if not isfile(config_filename): - print("You need to configure knotctl before proceeding") - run_config(config_filename) - - configcmd = get_config(config_filename) - baseurl = configcmd["baseurl"] - username = configcmd["username"] - password = configcmd["password"] - - # Authenticate - basic = HTTPBasicAuth(username, password) - response = requests.get(baseurl + "/user/login", auth=basic) - try: - token = response.json()["token"] - except KeyError: - output(response.json()) - return 1 - headers = {"Authorization": "Bearer {}".format(token)} - # Grab user input parser = argparse.ArgumentParser() parser.add_argument("--json", action=argparse.BooleanOptionalAction) @@ -187,6 +174,9 @@ def main() -> int: addcmd.add_argument("-t", "--ttl", required=True) addcmd.add_argument("-z", "--zone", required=True) + completecmd = subparsers.add_parser("complete") + completecmd.add_argument("-s", "--shell") + configcmd = subparsers.add_parser("config") configcmd.add_argument("-b", "--baseurl") configcmd.add_argument("-p", "--password") @@ -213,6 +203,36 @@ def main() -> int: argcomplete.autocomplete(parser) args = parser.parse_args() + if args.command == "complete": + run_complete(args.shell) + return 0 + + # Make sure we have config + config_basepath = join(environ["HOME"], ".knot") + config_filename = join(config_basepath, "config") + + if not isdir(config_basepath): + mkdir(config_basepath) + + if not isfile(config_filename): + if args.command != "config": + print("You need to configure knotctl before proceeding") + run_config(config_filename) + + config = get_config(config_filename) + baseurl = config["baseurl"] + username = config["username"] + password = config["password"] + + # Authenticate + basic = HTTPBasicAuth(username, password) + response = requests.get(baseurl + "/user/login", auth=basic) + try: + token = response.json()["token"] + except KeyError: + output(response.json()) + return 1 + headers = {"Authorization": "Bearer {}".format(token)} # Route based on command ttl = None