diff --git a/pillar_tool/ptcli/__init__.py b/pillar_tool/ptcli/__init__.py index b668da9..e69de29 100644 --- a/pillar_tool/ptcli/__init__.py +++ b/pillar_tool/ptcli/__init__.py @@ -1 +0,0 @@ -from .main import main \ No newline at end of file diff --git a/pillar_tool/ptcli/__main__.py b/pillar_tool/ptcli/__main__.py new file mode 100644 index 0000000..9fa17d5 --- /dev/null +++ b/pillar_tool/ptcli/__main__.py @@ -0,0 +1,7 @@ +from pillar_tool.ptcli.cli.cli_main import main + +def tool_main(): + main() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pillar_tool/ptcli/cli/__init__.py b/pillar_tool/ptcli/cli/__init__.py new file mode 100644 index 0000000..f6f1c70 --- /dev/null +++ b/pillar_tool/ptcli/cli/__init__.py @@ -0,0 +1,6 @@ +from .host import host +from .hostgroup import hostgroup +from .query import query +from .state import state +from .pillar import pillar +from .environment import environment \ No newline at end of file diff --git a/pillar_tool/ptcli/cli/cli_main.py b/pillar_tool/ptcli/cli/cli_main.py new file mode 100644 index 0000000..3b43fc2 --- /dev/null +++ b/pillar_tool/ptcli/cli/cli_main.py @@ -0,0 +1,41 @@ +import base64 + +import click + +import requests +from urllib.parse import quote + +from pillar_tool.schemas import HostCreateParams, HostgroupParams +from pillar_tool.util import config, load_config, Config +from pillar_tool.util.validation import split_and_validate_path, validate_fqdn + +cfg: Config | None = None +_base_url: str | None = None +_auth_header: dict[str, str] | None = None + +def auth_header(): + global _auth_header + return _auth_header + +def base_url(): + global _base_url + return _base_url + +@click.group("command") +def main(): + global cfg, _base_url, _auth_header + + # load the configuration and store it + load_config() + cfg = config() + _base_url = f"{cfg.ptcli.scheme}://{cfg.ptcli.host}:{cfg.ptcli.port}" + _auth_header = { 'Authorization': f"Basic {base64.b64encode(f"{cfg.ptcli.user}:{cfg.ptcli.password}".encode('utf-8')).decode('ascii')}" } + + # health check of the api + try: + response = requests.get(f"{_base_url}/health") + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"API seems to be unhealthy:\n{e}") + except requests.exceptions.ConnectionError as e: + raise click.ClickException("Unable to connect to PillarTool API") \ No newline at end of file diff --git a/pillar_tool/ptcli/cli/environment.py b/pillar_tool/ptcli/cli/environment.py new file mode 100644 index 0000000..f839b60 --- /dev/null +++ b/pillar_tool/ptcli/cli/environment.py @@ -0,0 +1,9 @@ +import click +import requests + +from .cli_main import main, auth_header, base_url + + +@main.group("environment") +def environment(): + pass \ No newline at end of file diff --git a/pillar_tool/ptcli/cli/host.py b/pillar_tool/ptcli/cli/host.py new file mode 100644 index 0000000..0974315 --- /dev/null +++ b/pillar_tool/ptcli/cli/host.py @@ -0,0 +1,57 @@ +import click +import requests + +from .cli_main import main, auth_header, base_url +from ...schemas import HostCreateParams +from ...util.validation import validate_fqdn + + +@main.group("host") +def host(): + pass + + +@host.command("list") +def host_list(): + click.echo("Listing known hosts...") + try: + response = requests.get(f"{base_url()}/host", headers=auth_header()) + response.raise_for_status() + + for h in response.json(): + click.echo(f" - {h}") + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to list hosts:\n{e}") + +@host.command("create") +@click.argument("fqdn") +@click.argument("parent", default=None) +def host_create(fqdn: str, parent: str | None): + click.echo("Creating host...") + try: + params = HostCreateParams( + parent=parent + ) + response = requests.post(f"{base_url()}/host/{fqdn}", json=params.model_dump(), headers=auth_header()) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to create host:\n{e}") + + click.echo(f"Host '{fqdn}' created!") + + +@host.command("delete") +@click.argument("fqdn") +def host_delete(fqdn: str): + if not validate_fqdn(fqdn): + click.echo("Invalid FQDN") + return + + if click.confirm(f"Are you sure you want to delete '{fqdn}'?"): + click.echo("Deleting host...") + + try: + response = requests.delete(f'{base_url}/host/{fqdn}', headers=auth_header()) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to delete host:\n{e}") diff --git a/pillar_tool/ptcli/cli/hostgroup.py b/pillar_tool/ptcli/cli/hostgroup.py new file mode 100644 index 0000000..8fe836e --- /dev/null +++ b/pillar_tool/ptcli/cli/hostgroup.py @@ -0,0 +1,74 @@ +import click +import requests + +from .cli_main import main, auth_header, base_url +from ...schemas import HostgroupParams +from ...util.validation import split_and_validate_path + + +@main.group("hostgroup") +def hostgroup(): + pass + + +@hostgroup.command("list") +def hostgroup_list(): + click.echo("Listing known hostgroups...") + try: + response = requests.get(f'{base_url}/hostgroup', headers=auth_header()) + response.raise_for_status() + + click.echo("Hostgroups:") + for hg in response.json(): + click.echo(f" - {hg}") + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to list hostgroups:\n{e}") + + +@hostgroup.command("show") +@click.argument("path") +def hostgroup_show(path: str): + click.echo(f"Showing hostgroup '{path}'...") + try: + labels = split_and_validate_path(path) + name = labels[-1] + path = '/'.join(labels[:-1]) if len(labels) > 1 else None + data = HostgroupParams( + path=path + ) + response = requests.get(f'{base_url}/hostgroup/{name}', headers=auth_header(), params=data.model_dump()) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to show hostgroup:\n{e}") + + +@hostgroup.command("create") +@click.argument("path") +def hostgroup_create(path: str): + click.echo(f"Creating hostgroup '{path}'...") + try: + labels = split_and_validate_path(path) + path = "/".join(labels[:-1]) if len(labels) > 1 else '' + name = labels[-1] + data = HostgroupParams( + path=path + ) + response = requests.post(f'{base_url}/hostgroup/{name}', headers=auth_header(), json=data.model_dump()) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to create hostgroup:\n{e}") + + +@hostgroup.command("delete") +@click.argument("path") +def hostgroup_delete(path: str): + click.echo(f"Deleting hostgroup {path}...") + try: + labels = split_and_validate_path(path) + name = labels[-1] + prefix = "/".join(labels[:-1]) if len(labels) > 1 else None + query_params = f"?path={prefix}" if prefix is not None else '' + response = requests.delete(f'{base_url}/hostgroup/{name}{query_params}', headers=auth_header()) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise click.ClickException(f"Failed to delete hostgroup:\n{e}") diff --git a/pillar_tool/ptcli/cli/pillar.py b/pillar_tool/ptcli/cli/pillar.py new file mode 100644 index 0000000..dffe677 --- /dev/null +++ b/pillar_tool/ptcli/cli/pillar.py @@ -0,0 +1,9 @@ +import click +import requests + +from .cli_main import main, auth_header, base_url + + +@main.group("pillar") +def pillar(): + pass \ No newline at end of file diff --git a/pillar_tool/ptcli/cli/query.py b/pillar_tool/ptcli/cli/query.py new file mode 100644 index 0000000..0d5fc29 --- /dev/null +++ b/pillar_tool/ptcli/cli/query.py @@ -0,0 +1,9 @@ +import click +import requests + +from .cli_main import main, auth_header, base_url + + +@main.group("query") +def query(): + pass \ No newline at end of file diff --git a/pillar_tool/ptcli/cli/state.py b/pillar_tool/ptcli/cli/state.py new file mode 100644 index 0000000..a29ef3e --- /dev/null +++ b/pillar_tool/ptcli/cli/state.py @@ -0,0 +1,9 @@ +import click +import requests + +from .cli_main import main, auth_header, base_url + + +@main.group("state") +def state(): + pass \ No newline at end of file diff --git a/pillar_tool/ptcli/cli_main.py b/pillar_tool/ptcli/cli_main.py deleted file mode 100644 index e69de29..0000000 diff --git a/pillar_tool/ptcli/host.py b/pillar_tool/ptcli/host.py deleted file mode 100644 index e69de29..0000000 diff --git a/pillar_tool/ptcli/hostgroup.py b/pillar_tool/ptcli/hostgroup.py deleted file mode 100644 index e69de29..0000000 diff --git a/pillar_tool/ptcli/main.py b/pillar_tool/ptcli/main.py deleted file mode 100644 index 1118317..0000000 --- a/pillar_tool/ptcli/main.py +++ /dev/null @@ -1,171 +0,0 @@ -import base64 - -import click - -import requests -from urllib.parse import quote - -from pillar_tool.schemas import HostCreateParams, HostgroupParams -from pillar_tool.util import config, load_config, Config -from pillar_tool.util.validation import split_and_validate_path, validate_fqdn - -cfg: Config | None = None -base_url: str | None = None -auth_header: dict[str, str] | None = None - - -@click.group("command") -def main(): - global cfg, base_url, auth_header - - # load the configuration and store it - load_config() - cfg = config() - base_url = f"{cfg.ptcli.scheme}://{cfg.ptcli.host}:{cfg.ptcli.port}" - auth_header = { 'Authorization': f"Basic {base64.b64encode(f"{cfg.ptcli.user}:{cfg.ptcli.password}".encode('utf-8')).decode('ascii')}" } - - # health check of the api - try: - response = requests.get(f"{base_url}/health") - response.raise_for_status() - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"API seems to be unhealthy:\n{e}") - except requests.exceptions.ConnectionError as e: - raise click.ClickException("Unable to connect to PillarTool API") - -@main.group("pillar") -def pillar(): - pass - -@main.group("host") -def host(): - pass - -@main.group("hostgroup") -def hostgroup(): - pass - -@main.group("environment") -def environment(): - pass - -@main.group("query") -def query(): - pass - -@pillar.command("get") -def pillar_get(): - print("pillar_get") - -@pillar.command("list") -def pillar_list(): - print("pillar_list") - -@host.command("list") -def host_list(): - click.echo("Listing known hosts...") - try: - response = requests.get(f"{base_url}/host", headers=auth_header) - response.raise_for_status() - - for h in response.json(): - click.echo(f" - {h}") - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to list hosts:\n{e}") - -@host.command("create") -@click.argument("fqdn") -@click.argument("parent", default=None) -def host_create(fqdn: str, parent: str | None): - click.echo("Creating host...") - try: - params = HostCreateParams( - parent=parent - ) - response = requests.post(f"{base_url}/host/{fqdn}", json=params.model_dump(), headers=auth_header) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to create host:\n{e}") - - click.echo(f"Host '{fqdn}' created!") - - -@host.command("delete") -@click.argument("fqdn") -def host_delete(fqdn: str): - if not validate_fqdn(fqdn): - click.echo("Invalid FQDN") - return - - if click.confirm(f"Are you sure you want to delete '{fqdn}'?"): - click.echo("Deleting host...") - - try: - response = requests.delete(f'{base_url}/host/{fqdn}', headers=auth_header) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to delete host:\n{e}") - - -@hostgroup.command("list") -def hostgroup_list(): - click.echo("Listing known hostgroups...") - try: - response = requests.get(f'{base_url}/hostgroup', headers=auth_header) - response.raise_for_status() - - click.echo("Hostgroups:") - for hg in response.json(): - click.echo(f" - {hg}") - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to list hostgroups:\n{e}") - - -@hostgroup.command("show") -@click.argument("path") -def hostgroup_show(path: str): - click.echo(f"Showing hostgroup '{path}'...") - try: - labels = split_and_validate_path(path) - name = labels[-1] - path = '/'.join(labels[:-1]) if len(labels) > 1 else None - data = HostgroupParams( - path=path - ) - response = requests.get(f'{base_url}/hostgroup/{name}', headers=auth_header, params=data.model_dump()) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to show hostgroup:\n{e}") - - -@hostgroup.command("create") -@click.argument("path") -def hostgroup_create(path: str): - click.echo(f"Creating hostgroup '{path}'...") - try: - labels = split_and_validate_path(path) - path = "/".join(labels[:-1]) if len(labels) > 1 else '' - name = labels[-1] - data = HostgroupParams( - path=path - ) - response = requests.post(f'{base_url}/hostgroup/{name}', headers=auth_header, json=data.model_dump()) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to create hostgroup:\n{e}") - - -@hostgroup.command("delete") -@click.argument("path") -def hostgroup_delete(path: str): - click.echo(f"Deleting hostgroup {path}...") - try: - labels = split_and_validate_path(path) - name = labels[-1] - prefix = "/".join(labels[:-1]) if len(labels) > 1 else None - query_params = f"?path={prefix}" if prefix is not None else '' - response = requests.delete(f'{base_url}/hostgroup/{name}{query_params}', headers=auth_header) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - raise click.ClickException(f"Failed to delete hostgroup:\n{e}") - diff --git a/pillartool.egg-info/SOURCES.txt b/pillartool.egg-info/SOURCES.txt index 74b0c1d..38f4141 100644 --- a/pillartool.egg-info/SOURCES.txt +++ b/pillartool.egg-info/SOURCES.txt @@ -26,14 +26,20 @@ pillar_tool/db/queries/__init__.py pillar_tool/db/queries/auth_queries.py pillar_tool/db/queries/host_queries.py pillar_tool/db/queries/pillar_queries.py -pillar_tool/frontend/__init__.py -pillar_tool/frontend/pillar_view.py pillar_tool/middleware/__init__.py pillar_tool/middleware/basicauth_backend.py pillar_tool/middleware/db_connection.py pillar_tool/middleware/logging.py pillar_tool/ptcli/__init__.py -pillar_tool/ptcli/main.py +pillar_tool/ptcli/__main__.py +pillar_tool/ptcli/cli/__init__.py +pillar_tool/ptcli/cli/cli_main.py +pillar_tool/ptcli/cli/environment.py +pillar_tool/ptcli/cli/host.py +pillar_tool/ptcli/cli/hostgroup.py +pillar_tool/ptcli/cli/pillar.py +pillar_tool/ptcli/cli/query.py +pillar_tool/ptcli/cli/state.py pillar_tool/routers/__init__.py pillar_tool/routers/environment.py pillar_tool/routers/host.py diff --git a/pillartool.egg-info/entry_points.txt b/pillartool.egg-info/entry_points.txt index e69bdeb..1f04070 100644 --- a/pillartool.egg-info/entry_points.txt +++ b/pillartool.egg-info/entry_points.txt @@ -1,2 +1,2 @@ [console_scripts] -ptcli = pillar_tool.ptcli:main +ptcli = pillar_tool.ptcli.__main__:tool_main diff --git a/pyproject.toml b/pyproject.toml index 8c9a117..dc61e84 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,4 +18,4 @@ dependencies = [ [project.scripts] -ptcli = "pillar_tool.ptcli:main" +ptcli = "pillar_tool.ptcli.__main__:tool_main"