impreza-cli 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- impreza_cli/__init__.py +14 -0
- impreza_cli/commands/__init__.py +7 -0
- impreza_cli/commands/_helpers.py +128 -0
- impreza_cli/commands/account.py +576 -0
- impreza_cli/commands/catalog.py +270 -0
- impreza_cli/commands/context.py +232 -0
- impreza_cli/commands/doctor.py +427 -0
- impreza_cli/commands/domain.py +858 -0
- impreza_cli/commands/invoice.py +198 -0
- impreza_cli/commands/key.py +104 -0
- impreza_cli/commands/orders.py +478 -0
- impreza_cli/commands/services.py +100 -0
- impreza_cli/commands/vps.py +812 -0
- impreza_cli/commands/vps_cloud.py +865 -0
- impreza_cli/commands/vps_proxmox.py +727 -0
- impreza_cli/commands/webhooks.py +483 -0
- impreza_cli/config.py +405 -0
- impreza_cli/main.py +100 -0
- impreza_cli/output.py +207 -0
- impreza_cli/sdk.py +97 -0
- impreza_cli/state.py +94 -0
- impreza_cli-0.3.0.dist-info/METADATA +296 -0
- impreza_cli-0.3.0.dist-info/RECORD +26 -0
- impreza_cli-0.3.0.dist-info/WHEEL +5 -0
- impreza_cli-0.3.0.dist-info/entry_points.txt +2 -0
- impreza_cli-0.3.0.dist-info/top_level.txt +1 -0
impreza_cli/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Impreza Host CLI — official command-line interface for the
|
|
2
|
+
Impreza Host public REST API.
|
|
3
|
+
|
|
4
|
+
Phase 2.1 ships the multi-context machinery (config file at
|
|
5
|
+
``$XDG_CONFIG_HOME/impreza/config.toml`` on Linux/macOS,
|
|
6
|
+
``%APPDATA%\\impreza\\config.toml`` on Windows) and the
|
|
7
|
+
``impreza context`` subcommand surface. Subsequent fases (2.2+)
|
|
8
|
+
add the read-only resource commands on top.
|
|
9
|
+
|
|
10
|
+
Built on top of :mod:`impreza` (the SDK), so the network layer,
|
|
11
|
+
auth, retry, and error handling are inherited.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0a0"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Subcommand modules — one per resource group.
|
|
2
|
+
|
|
3
|
+
Each module exposes an ``app`` (a ``typer.Typer`` instance) that
|
|
4
|
+
``impreza_cli.main`` mounts under the right command name. Phase 2.1
|
|
5
|
+
ships :mod:`.context`. Subsequent fases add ``account``, ``catalog``,
|
|
6
|
+
``domain``, ``vps``, ``invoice``, ``key``, etc.
|
|
7
|
+
"""
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Shared helpers used across CLI commands.
|
|
2
|
+
|
|
3
|
+
Originally introduced as ``_vps_helpers.py`` in Phase 3.4 to break
|
|
4
|
+
a circular-import between ``commands/vps.py`` and the per-backend
|
|
5
|
+
sub-resource modules. Renamed to ``_helpers.py`` in Phase 4.3
|
|
6
|
+
because the helpers are not VPS-specific — every command that
|
|
7
|
+
catches :class:`~impreza.exceptions.ApiError` benefits from
|
|
8
|
+
routing through :func:`exit_on_api_error`.
|
|
9
|
+
|
|
10
|
+
Conventions kept consistent across every CLI command:
|
|
11
|
+
|
|
12
|
+
* :func:`exit_on_api_error` — the canonical mapping of
|
|
13
|
+
:class:`~impreza.exceptions.ApiError` to a single stderr line plus
|
|
14
|
+
``raise typer.Exit(1)``. The 5 commands that grew their own local
|
|
15
|
+
copy during Phase 2 (account, catalog, domain, invoice, key)
|
|
16
|
+
consolidated onto this single import in 4.3.
|
|
17
|
+
* :func:`resolve_vps_or_exit` — bound-model lookup that maps
|
|
18
|
+
``ResourceNotFound`` and "service exists but is not a VPS"
|
|
19
|
+
(:class:`InvalidRequest`) to the same friendly stderr lines the
|
|
20
|
+
3.2 power verbs introduced. VPS-only by design.
|
|
21
|
+
* :func:`wait_for_operation` — the dotted-progress poll loop used
|
|
22
|
+
by reinstall / migrate (3.3) and by snapshots-rollback / backups-
|
|
23
|
+
create / backups-restore (3.4 onwards). Reimplements
|
|
24
|
+
``op.wait()`` because the SDK helper is silent — for long-running
|
|
25
|
+
operations, silence reads as a hang.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import time
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
33
|
+
import typer
|
|
34
|
+
from impreza import Operation, Vps
|
|
35
|
+
from impreza.exceptions import ApiError, InvalidRequest, ResourceNotFound
|
|
36
|
+
|
|
37
|
+
from ..output import error
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def exit_on_api_error(exc: ApiError) -> None:
|
|
41
|
+
"""Render a one-line error from an SDK :class:`ApiError` to
|
|
42
|
+
stderr and ``raise typer.Exit(1)``.
|
|
43
|
+
|
|
44
|
+
Includes the upstream ``code`` and request id when present so
|
|
45
|
+
bug reports can be triaged without re-running with ``--debug``.
|
|
46
|
+
Never returns — the ``raise`` is part of the contract.
|
|
47
|
+
"""
|
|
48
|
+
parts = [exc.message]
|
|
49
|
+
if exc.code:
|
|
50
|
+
parts.append(f"(code={exc.code})")
|
|
51
|
+
if exc.request_id:
|
|
52
|
+
parts.append(f"[request_id={exc.request_id}]")
|
|
53
|
+
error(" ".join(parts))
|
|
54
|
+
raise typer.Exit(code=1)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def resolve_vps_or_exit(client: Any, service_id: int) -> Vps:
|
|
58
|
+
"""Look up a VPS by id, mapping the standard SDK errors to the
|
|
59
|
+
same friendly stderr lines the 3.2 power verbs introduced.
|
|
60
|
+
|
|
61
|
+
Returns the bound :class:`~impreza.Vps` model (``VpsProxmox`` or
|
|
62
|
+
``VpsCloud``). Callers can then dispatch to the right SDK method
|
|
63
|
+
without caring which backend they're on.
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
return client.vps.get(service_id)
|
|
67
|
+
except ResourceNotFound:
|
|
68
|
+
error(f"VPS service {service_id} not found on this account.")
|
|
69
|
+
raise typer.Exit(code=1) from None
|
|
70
|
+
except InvalidRequest as exc:
|
|
71
|
+
# "Service exists but isn't a VPS" — the SDK message is
|
|
72
|
+
# already friendly, pass it through.
|
|
73
|
+
exit_on_api_error(exc)
|
|
74
|
+
raise # unreachable — exit_on_api_error always raises
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def wait_for_operation(
|
|
78
|
+
op: Operation,
|
|
79
|
+
*,
|
|
80
|
+
label: str,
|
|
81
|
+
timeout: int,
|
|
82
|
+
poll_interval: float = 2.0,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Block on an :class:`Operation` future, printing one dot per
|
|
85
|
+
poll cycle so the user gets visible feedback during long
|
|
86
|
+
upstream queue runs.
|
|
87
|
+
|
|
88
|
+
Reimplements ``op.wait()`` rather than calling it because the
|
|
89
|
+
SDK helper is silent — for an operation that can run minutes
|
|
90
|
+
(reinstall, migrate, backup create/restore, snapshot rollback),
|
|
91
|
+
silence reads as a hang. Maps the SDK's
|
|
92
|
+
:class:`OperationFailed` / :class:`OperationTimeout` paths to
|
|
93
|
+
clean CLI errors with a remediation hint pointing at
|
|
94
|
+
``--timeout``.
|
|
95
|
+
"""
|
|
96
|
+
typer.echo(f"{label} (operation {op.uuid[:12]}) ", nl=False)
|
|
97
|
+
elapsed = 0.0
|
|
98
|
+
while not op.is_done():
|
|
99
|
+
if elapsed >= timeout:
|
|
100
|
+
typer.echo()
|
|
101
|
+
error(
|
|
102
|
+
f"Operation {op.uuid} did not finish within "
|
|
103
|
+
f"{timeout}s (last status: {op.status!r}). "
|
|
104
|
+
"Re-run with a larger --timeout to wait longer."
|
|
105
|
+
)
|
|
106
|
+
raise typer.Exit(code=1)
|
|
107
|
+
typer.echo(".", nl=False)
|
|
108
|
+
time.sleep(poll_interval)
|
|
109
|
+
elapsed += poll_interval
|
|
110
|
+
try:
|
|
111
|
+
op.refresh()
|
|
112
|
+
except ApiError as exc:
|
|
113
|
+
typer.echo()
|
|
114
|
+
exit_on_api_error(exc)
|
|
115
|
+
typer.echo(" done.")
|
|
116
|
+
if op.is_failure():
|
|
117
|
+
msg = f"Operation {op.uuid} ended in {op.status!r}"
|
|
118
|
+
if op.error:
|
|
119
|
+
msg += f" — {op.error}"
|
|
120
|
+
error(msg + ".")
|
|
121
|
+
raise typer.Exit(code=1)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
__all__ = [
|
|
125
|
+
"exit_on_api_error",
|
|
126
|
+
"resolve_vps_or_exit",
|
|
127
|
+
"wait_for_operation",
|
|
128
|
+
]
|