impreza-cli 0.3.0__tar.gz → 0.3.2__tar.gz
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-0.3.0 → impreza_cli-0.3.2}/PKG-INFO +3 -3
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/README.md +2 -2
- impreza_cli-0.3.2/impreza_cli/__init__.py +24 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/vps_cloud.py +30 -123
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/PKG-INFO +3 -3
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_5_smoke.py +18 -68
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_vps_cloud_commands.py +14 -57
- impreza_cli-0.3.0/impreza_cli/__init__.py +0 -14
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/__init__.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/_helpers.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/account.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/catalog.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/context.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/doctor.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/domain.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/invoice.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/key.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/orders.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/services.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/vps.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/vps_proxmox.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/webhooks.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/config.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/main.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/output.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/sdk.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/state.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/SOURCES.txt +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/dependency_links.txt +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/entry_points.txt +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/requires.txt +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/top_level.txt +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/pyproject.toml +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/setup.cfg +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/.gitkeep +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/__init__.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/conftest.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_account_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_account_topup_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_catalog_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_config.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_context_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_doctor_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_domain_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_invoice_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_key_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_orders_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_2_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_3_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_4_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_5_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_6_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_1_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_2_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_3_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_4_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_6_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_7_smoke.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_services_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_vps_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_vps_proxmox_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_webhooks_commands.py +0 -0
- {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_yaml_output.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impreza-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Official command-line interface for the Impreza Host public REST API
|
|
5
5
|
Author-email: Impreza Host <support@imprezahost.com>
|
|
6
6
|
License: MIT
|
|
@@ -142,10 +142,10 @@ to see the full subcommand list, or `impreza <group> <command>
|
|
|
142
142
|
| `account` | `info / balance / services / topup / topup-status` | Profile + balance + services + crypto top-up |
|
|
143
143
|
| `catalog` | `products / product / product-groups / tlds` | Pre-purchase discovery |
|
|
144
144
|
| `domain` | `show / check / pricing / register / transfer / set-nameservers / lock / unlock / id-protection / raa-verify / gdpr-auth / transfer-approval` + `domain dns list / add / update / delete / activate` | Domain registrations + full DNS CRUD |
|
|
145
|
-
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso /
|
|
145
|
+
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
|
|
146
146
|
| `order` | `list / show / create / upgrade` | Submit / browse product orders |
|
|
147
147
|
| `service` | `cancel` | Submit cancellation request (any service) |
|
|
148
|
-
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history |
|
|
148
|
+
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history. Event-payload contract documented in [`../openapi/asyncapi.yaml`](../openapi/asyncapi.yaml). |
|
|
149
149
|
| `invoice` | `list / show` | Invoices with line items + transactions |
|
|
150
150
|
| `key` | `whoami` | Active API key identity + IP whitelist |
|
|
151
151
|
|
|
@@ -101,10 +101,10 @@ to see the full subcommand list, or `impreza <group> <command>
|
|
|
101
101
|
| `account` | `info / balance / services / topup / topup-status` | Profile + balance + services + crypto top-up |
|
|
102
102
|
| `catalog` | `products / product / product-groups / tlds` | Pre-purchase discovery |
|
|
103
103
|
| `domain` | `show / check / pricing / register / transfer / set-nameservers / lock / unlock / id-protection / raa-verify / gdpr-auth / transfer-approval` + `domain dns list / add / update / delete / activate` | Domain registrations + full DNS CRUD |
|
|
104
|
-
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso /
|
|
104
|
+
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
|
|
105
105
|
| `order` | `list / show / create / upgrade` | Submit / browse product orders |
|
|
106
106
|
| `service` | `cancel` | Submit cancellation request (any service) |
|
|
107
|
-
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history |
|
|
107
|
+
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history. Event-payload contract documented in [`../openapi/asyncapi.yaml`](../openapi/asyncapi.yaml). |
|
|
108
108
|
| `invoice` | `list / show` | Invoices with line items + transactions |
|
|
109
109
|
| `key` | `whoami` | Active API key identity + IP whitelist |
|
|
110
110
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Impreza Host CLI — official command-line interface for the
|
|
2
|
+
Impreza Host public REST API.
|
|
3
|
+
|
|
4
|
+
Multi-context machinery (config file at
|
|
5
|
+
``$XDG_CONFIG_HOME/impreza/config.toml`` on Linux/macOS,
|
|
6
|
+
``%APPDATA%\\impreza\\config.toml`` on Windows) plus the
|
|
7
|
+
``impreza context`` subcommand surface and 65 verbs across 11
|
|
8
|
+
command groups.
|
|
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
|
+
# Read the installed package version from metadata. Always matches
|
|
15
|
+
# `pip show impreza-cli`, so `impreza --version` can't drift from
|
|
16
|
+
# the wheel even if someone forgets to bump a hard-coded string.
|
|
17
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
__version__ = _pkg_version("impreza-cli")
|
|
21
|
+
except PackageNotFoundError: # source checkout without an install
|
|
22
|
+
__version__ = "0.0.0+unknown"
|
|
23
|
+
|
|
24
|
+
del _pkg_version, PackageNotFoundError
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Cloud sub-resources mounted on the main ``vps`` Typer
|
|
4
4
|
app as the ``cloud`` namespace. Mirror of the 3.4 Proxmox layout.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Four nested sub-apps + five inline verbs at the cloud root:
|
|
7
7
|
|
|
8
8
|
* ``vps cloud images`` — ``list / create / restore / delete``
|
|
9
9
|
Account-scoped on the wire (the Cloud image catalog is
|
|
@@ -15,15 +15,22 @@ Five nested sub-apps + five inline verbs at the cloud root:
|
|
|
15
15
|
|
|
16
16
|
* ``vps cloud iso`` — ``mount / unmount``
|
|
17
17
|
|
|
18
|
-
* ``vps cloud rdns`` — ``get / set / delete``
|
|
19
|
-
Account-scoped on the API side (``/vps/cloud/rdns/{ip}``); the
|
|
20
|
-
SDK exposes it via the bound VPS for ergonomics. ``<ip>`` selects
|
|
21
|
-
the record within the account.
|
|
22
|
-
|
|
23
18
|
* ``vps cloud ssh-keys`` — ``list / assign``
|
|
24
19
|
``list`` returns every key on the Cloud account; ``assign``
|
|
25
20
|
attaches one or more existing keys to the bound VPS.
|
|
26
21
|
|
|
22
|
+
* ``vps cloud rdns`` (``get / set / delete``) was previously here
|
|
23
|
+
but is **removed from the CLI surface in 7.7**. The
|
|
24
|
+
``/vps/cloud/rdns/{ip}`` endpoint hits a WAF/mod_security rule
|
|
25
|
+
on the public edge (dotted-IPv4 path segments are flagged as
|
|
26
|
+
suspicious) and returns the WAF maintenance HTML page instead
|
|
27
|
+
of a JSON response. Server-side fix is owned by whoever
|
|
28
|
+
configures the WAF rules; until that lands, the CLI hides the
|
|
29
|
+
broken verbs rather than ship a known-broken surface. The SDK
|
|
30
|
+
resource (``vps.rdns.get/set/delete``) stays intact so library
|
|
31
|
+
consumers can still call it and handle the WAF response
|
|
32
|
+
themselves.
|
|
33
|
+
|
|
27
34
|
Inline verbs (no sub-app):
|
|
28
35
|
|
|
29
36
|
* ``vps cloud vnc <id>`` — read the VNC client triple (host, port,
|
|
@@ -65,7 +72,7 @@ from ._helpers import exit_on_api_error, resolve_vps_or_exit
|
|
|
65
72
|
|
|
66
73
|
app = typer.Typer(
|
|
67
74
|
name="cloud",
|
|
68
|
-
help="Cloud-only sub-resources: images, rescue, iso,
|
|
75
|
+
help="Cloud-only sub-resources: images, rescue, iso, ssh-keys, etc.",
|
|
69
76
|
no_args_is_help=True,
|
|
70
77
|
)
|
|
71
78
|
|
|
@@ -84,11 +91,6 @@ iso_app = typer.Typer(
|
|
|
84
91
|
help="Mount / unmount an ISO on a Cloud VPS.",
|
|
85
92
|
no_args_is_help=True,
|
|
86
93
|
)
|
|
87
|
-
rdns_app = typer.Typer(
|
|
88
|
-
name="rdns",
|
|
89
|
-
help="Reverse-DNS records for Cloud account IPs.",
|
|
90
|
-
no_args_is_help=True,
|
|
91
|
-
)
|
|
92
94
|
ssh_keys_app = typer.Typer(
|
|
93
95
|
name="ssh-keys",
|
|
94
96
|
help="List account-level SSH keys and assign to a Cloud VPS.",
|
|
@@ -100,10 +102,11 @@ ipv6_app = typer.Typer(
|
|
|
100
102
|
no_args_is_help=True,
|
|
101
103
|
)
|
|
102
104
|
|
|
105
|
+
# rdns sub-app removed in 7.7 (WAF-blocked endpoint). SDK resource
|
|
106
|
+
# `vps.rdns.get/set/delete` stays available for library consumers.
|
|
103
107
|
app.add_typer(images_app)
|
|
104
108
|
app.add_typer(rescue_app)
|
|
105
109
|
app.add_typer(iso_app)
|
|
106
|
-
app.add_typer(rdns_app)
|
|
107
110
|
app.add_typer(ssh_keys_app)
|
|
108
111
|
app.add_typer(ipv6_app)
|
|
109
112
|
|
|
@@ -448,117 +451,21 @@ def iso_unmount(
|
|
|
448
451
|
|
|
449
452
|
|
|
450
453
|
# ══════════════════════════════════════════════════════════════════════
|
|
451
|
-
# rDNS
|
|
454
|
+
# rDNS — removed from CLI surface in 7.7
|
|
452
455
|
# ══════════════════════════════════════════════════════════════════════
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
output: OutputFormat | None = typer.Option(
|
|
467
|
-
None, "--output", "-o",
|
|
468
|
-
help="Output format. Overrides the global --output flag.",
|
|
469
|
-
case_sensitive=False,
|
|
470
|
-
),
|
|
471
|
-
) -> None:
|
|
472
|
-
"""Read the rDNS record for an IP.
|
|
473
|
-
|
|
474
|
-
Wraps ``vps.rdns.get(ip)``. The API path is account-scoped
|
|
475
|
-
(``GET /vps/cloud/rdns/{ip}``).
|
|
476
|
-
"""
|
|
477
|
-
state = from_typer_context(typer_ctx)
|
|
478
|
-
fmt = resolve_output(state, output)
|
|
479
|
-
with make_client_or_exit(state) as client:
|
|
480
|
-
vps = resolve_vps_or_exit(client, service_id)
|
|
481
|
-
try:
|
|
482
|
-
result = vps.rdns.get(ip)
|
|
483
|
-
except BackendNotSupported:
|
|
484
|
-
_cloud_only_exit("rdns")
|
|
485
|
-
return
|
|
486
|
-
except ApiError as exc:
|
|
487
|
-
exit_on_api_error(exc)
|
|
488
|
-
return
|
|
489
|
-
|
|
490
|
-
if not result:
|
|
491
|
-
typer.echo(f"No rDNS record for {ip}.")
|
|
492
|
-
return
|
|
493
|
-
print_dict(f"rDNS — {ip}", {str(k): v for k, v in result.items()}, fmt=fmt)
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
@rdns_app.command("set")
|
|
497
|
-
def rdns_set(
|
|
498
|
-
typer_ctx: typer.Context,
|
|
499
|
-
service_id: int = typer.Argument(
|
|
500
|
-
...,
|
|
501
|
-
help="Service id of any Cloud VPS on the account (see `rdns get`).",
|
|
502
|
-
),
|
|
503
|
-
ip: str = typer.Argument(..., help="IP address to set rDNS on."),
|
|
504
|
-
hostname: str = typer.Argument(..., help="Hostname to point the rDNS PTR at."),
|
|
505
|
-
) -> None:
|
|
506
|
-
"""Set the rDNS PTR record for an IP.
|
|
507
|
-
|
|
508
|
-
Wraps ``vps.rdns.set(ip, hostname)``. Some upstreams reject
|
|
509
|
-
hostnames that don't resolve forward to the IP — those return
|
|
510
|
-
a 4xx and the SDK surfaces it as :class:`InvalidRequest`.
|
|
511
|
-
"""
|
|
512
|
-
state = from_typer_context(typer_ctx)
|
|
513
|
-
with make_client_or_exit(state) as client:
|
|
514
|
-
vps = resolve_vps_or_exit(client, service_id)
|
|
515
|
-
try:
|
|
516
|
-
vps.rdns.set(ip, hostname)
|
|
517
|
-
except BackendNotSupported:
|
|
518
|
-
_cloud_only_exit("rdns")
|
|
519
|
-
return
|
|
520
|
-
except ApiError as exc:
|
|
521
|
-
exit_on_api_error(exc)
|
|
522
|
-
return
|
|
523
|
-
success(f"rDNS for {ip} set to {hostname!r}.")
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
@rdns_app.command("delete")
|
|
527
|
-
def rdns_delete(
|
|
528
|
-
typer_ctx: typer.Context,
|
|
529
|
-
service_id: int = typer.Argument(
|
|
530
|
-
...,
|
|
531
|
-
help="Service id of any Cloud VPS on the account (see `rdns get`).",
|
|
532
|
-
),
|
|
533
|
-
ip: str = typer.Argument(..., help="IP address whose rDNS to clear."),
|
|
534
|
-
yes: bool = typer.Option(
|
|
535
|
-
False, "--yes", "-y", help="Skip the deletion confirmation prompt."
|
|
536
|
-
),
|
|
537
|
-
) -> None:
|
|
538
|
-
"""Delete the rDNS record for an IP.
|
|
539
|
-
|
|
540
|
-
Wraps ``vps.rdns.delete(ip)``. After deletion, reverse-DNS
|
|
541
|
-
lookups on the IP return whatever default the upstream
|
|
542
|
-
configures.
|
|
543
|
-
"""
|
|
544
|
-
state = from_typer_context(typer_ctx)
|
|
545
|
-
confirm_or_exit(
|
|
546
|
-
f"Removing rDNS for {ip} reverts reverse-DNS lookups to the "
|
|
547
|
-
"upstream default. Mail servers and rate-limiters relying on "
|
|
548
|
-
"the PTR may behave differently.",
|
|
549
|
-
yes=yes,
|
|
550
|
-
)
|
|
551
|
-
with make_client_or_exit(state) as client:
|
|
552
|
-
vps = resolve_vps_or_exit(client, service_id)
|
|
553
|
-
try:
|
|
554
|
-
vps.rdns.delete(ip)
|
|
555
|
-
except BackendNotSupported:
|
|
556
|
-
_cloud_only_exit("rdns")
|
|
557
|
-
return
|
|
558
|
-
except ApiError as exc:
|
|
559
|
-
exit_on_api_error(exc)
|
|
560
|
-
return
|
|
561
|
-
success(f"rDNS removed for {ip}.")
|
|
456
|
+
#
|
|
457
|
+
# `rdns_app` + the get/set/delete commands lived here through 0.3.1.
|
|
458
|
+
# The /vps/cloud/rdns/{ip} endpoint hits a WAF rule on the public
|
|
459
|
+
# edge (dotted-IPv4 path segments) and returns HTML instead of JSON,
|
|
460
|
+
# so the verbs are unusable end-to-end. SDK methods stay available
|
|
461
|
+
# (`client.vps.get(id).rdns.get/set/delete`) for library consumers
|
|
462
|
+
# who want to handle the WAF response themselves.
|
|
463
|
+
#
|
|
464
|
+
# Re-add the typer app + commands here when the server-side WAF rule
|
|
465
|
+
# is fixed. The shape was three commands matching `vps.rdns.{get,
|
|
466
|
+
# set, delete}` with a service-id positional + `<ip>` (+ `<hostname>`
|
|
467
|
+
# for set) — see git history at tag `cli-v0.3.1` for the original
|
|
468
|
+
# code if you need to restore it.
|
|
562
469
|
|
|
563
470
|
|
|
564
471
|
# ══════════════════════════════════════════════════════════════════════
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impreza-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Official command-line interface for the Impreza Host public REST API
|
|
5
5
|
Author-email: Impreza Host <support@imprezahost.com>
|
|
6
6
|
License: MIT
|
|
@@ -142,10 +142,10 @@ to see the full subcommand list, or `impreza <group> <command>
|
|
|
142
142
|
| `account` | `info / balance / services / topup / topup-status` | Profile + balance + services + crypto top-up |
|
|
143
143
|
| `catalog` | `products / product / product-groups / tlds` | Pre-purchase discovery |
|
|
144
144
|
| `domain` | `show / check / pricing / register / transfer / set-nameservers / lock / unlock / id-protection / raa-verify / gdpr-auth / transfer-approval` + `domain dns list / add / update / delete / activate` | Domain registrations + full DNS CRUD |
|
|
145
|
-
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso /
|
|
145
|
+
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
|
|
146
146
|
| `order` | `list / show / create / upgrade` | Submit / browse product orders |
|
|
147
147
|
| `service` | `cancel` | Submit cancellation request (any service) |
|
|
148
|
-
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history |
|
|
148
|
+
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history. Event-payload contract documented in [`../openapi/asyncapi.yaml`](../openapi/asyncapi.yaml). |
|
|
149
149
|
| `invoice` | `list / show` | Invoices with line items + transactions |
|
|
150
150
|
| `key` | `whoami` | Active API key identity + IP whitelist |
|
|
151
151
|
|
|
@@ -8,24 +8,25 @@ Bucketed by reversibility, the same way 3.3 / 3.4 are:
|
|
|
8
8
|
smokes so a casual ``pytest`` run never touches the live API.
|
|
9
9
|
* **Catastrophic** (images create/restore/delete, resize,
|
|
10
10
|
boot-order, vnc-password rotate, rescue enable, iso mount,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
ipv6 enable): real state changes — some reversible
|
|
12
|
+
(boot-order, rescue, iso) and some that consume storage quota
|
|
13
|
+
(images create) or have billing impact (resize). Mock-only by
|
|
14
|
+
design.
|
|
15
15
|
|
|
16
16
|
The cheap round-trips that 3.3's set-hostname / 3.4's snapshots
|
|
17
|
-
create+delete enabled don't have direct
|
|
18
|
-
|
|
19
|
-
- rdns get is safe to read but requires knowing a valid IP
|
|
20
|
-
(the VPS's dedicated_ip from `vps show`). Could be done as
|
|
21
|
-
a round-trip (`set sentinel → get → delete-or-restore`)
|
|
22
|
-
but the upstream may reject hostnames that don't resolve
|
|
23
|
-
forward, so the test would need to construct a resolvable
|
|
24
|
-
sentinel. Deferred for now.
|
|
17
|
+
create+delete enabled don't have a direct equivalent here:
|
|
18
|
+
|
|
25
19
|
- vnc read returns short-lived credentials; testing them
|
|
26
20
|
would require an actual VNC client. Just exercise the
|
|
27
21
|
request-response.
|
|
28
22
|
|
|
23
|
+
The earlier ``vps cloud rdns`` smoke (read for the VPS's
|
|
24
|
+
dedicated_ip) was removed in 7.7 along with the CLI verbs —
|
|
25
|
+
the public-edge WAF rejects /vps/cloud/rdns/{ip} paths and
|
|
26
|
+
returns HTML instead of JSON. SDK-level unit tests for the
|
|
27
|
+
underlying ``vps.rdns`` resource still live in
|
|
28
|
+
sdk-python/tests/test_vps_specific_unit.py.
|
|
29
|
+
|
|
29
30
|
If we add reversible round-trips later, gate them behind
|
|
30
31
|
extra opt-in flags (``IMPREZA_TEST_ALLOW_RDNS_CHANGE=1`` etc.),
|
|
31
32
|
following the 3.3 ``IMPREZA_TEST_ALLOW_PASSWORD_RESET`` pattern.
|
|
@@ -175,59 +176,8 @@ def test_smoke_vnc_read(live_seeded_config: Path) -> None:
|
|
|
175
176
|
)
|
|
176
177
|
|
|
177
178
|
|
|
178
|
-
#
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
reason="reads from a real account",
|
|
184
|
-
)
|
|
185
|
-
def test_smoke_rdns_get_dedicated_ip(live_seeded_config: Path) -> None:
|
|
186
|
-
"""Best-effort: look up the VPS's dedicated_ip via `vps show`,
|
|
187
|
-
then read its rDNS. Skips cleanly if the VPS doesn't have a
|
|
188
|
-
dedicated_ip exposed (some flavours don't).
|
|
189
|
-
"""
|
|
190
|
-
vps_id = _test_cloud_vps_id()
|
|
191
|
-
|
|
192
|
-
show_result = runner.invoke(
|
|
193
|
-
app, ["vps", "show", str(vps_id), "--output", "json"]
|
|
194
|
-
)
|
|
195
|
-
if show_result.exit_code != 0:
|
|
196
|
-
pytest.fail(f"vps show failed: {show_result.stderr.strip()!r}")
|
|
197
|
-
info = json.loads(show_result.stdout)
|
|
198
|
-
dedicated_ip = info.get("dedicated_ip")
|
|
199
|
-
if not dedicated_ip:
|
|
200
|
-
pytest.skip(
|
|
201
|
-
f"VPS {vps_id} has no dedicated_ip in its Service record "
|
|
202
|
-
"— rDNS read needs a specific IP target"
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
result = runner.invoke(
|
|
206
|
-
app,
|
|
207
|
-
["vps", "cloud", "rdns", "get", str(vps_id), dedicated_ip,
|
|
208
|
-
"--output", "json"],
|
|
209
|
-
)
|
|
210
|
-
if result.exit_code != 0:
|
|
211
|
-
if "Proxmox backend" in result.stderr:
|
|
212
|
-
pytest.skip(f"VPS {vps_id} is on Proxmox — rdns is Cloud-only")
|
|
213
|
-
# rDNS-not-set is a legitimate state; the SDK returns an empty
|
|
214
|
-
# dict and the CLI prints "No rDNS record" on stdout. Treat
|
|
215
|
-
# any non-zero exit other than that as a failure.
|
|
216
|
-
if "No rDNS record" in result.stdout:
|
|
217
|
-
print(f"\n VPS {vps_id}: no rDNS record for {dedicated_ip}")
|
|
218
|
-
return
|
|
219
|
-
# the Cloud backend sometimes returns 502 instead of 404 when the
|
|
220
|
-
# rDNS record isn't set or the IP isn't in their managed pool.
|
|
221
|
-
# Treat as skip rather than failure — the integration plumbing
|
|
222
|
-
# is covered by unit tests with respx mocks.
|
|
223
|
-
if "502" in result.stderr or "UPSTREAM_ERROR" in result.stderr:
|
|
224
|
-
pytest.skip(
|
|
225
|
-
f"Cloud backend rejected rDNS read for {dedicated_ip} "
|
|
226
|
-
f"(upstream 502): {result.stderr.strip()!r}"
|
|
227
|
-
)
|
|
228
|
-
pytest.fail(f"rdns get failed: {result.stderr.strip()!r}")
|
|
229
|
-
if result.stdout.strip().startswith("{"):
|
|
230
|
-
parsed = json.loads(result.stdout)
|
|
231
|
-
print(f"\n VPS {vps_id}: rDNS for {dedicated_ip} = {parsed}")
|
|
232
|
-
else:
|
|
233
|
-
print(f"\n VPS {vps_id}: rDNS for {dedicated_ip} read OK (empty)")
|
|
179
|
+
# rdns smoke test removed in 7.7. The `vps cloud rdns get` verb is
|
|
180
|
+
# no longer exposed by the CLI (WAF-blocked endpoint — the public
|
|
181
|
+
# edge returns the maintenance HTML page for /vps/cloud/rdns/{ip}
|
|
182
|
+
# paths with dotted-IPv4 segments). Re-add when the server-side WAF
|
|
183
|
+
# rule is fixed and `rdns_app` returns to commands/vps_cloud.py.
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
"""Unit tests for ``impreza vps cloud`` (Phase 3.5).
|
|
1
|
+
"""Unit tests for ``impreza vps cloud`` (Phase 3.5; 7.7 trimmed rdns).
|
|
2
2
|
|
|
3
|
-
Mirrors the 3.4 Proxmox unit suite — covers the
|
|
4
|
-
sub-apps (images, rescue, iso,
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Mirrors the 3.4 Proxmox unit suite — covers the four nested
|
|
4
|
+
sub-apps (images, rescue, iso, ssh-keys) plus the five inline
|
|
5
|
+
verbs at the cloud root (vnc, vnc-password, resize, boot-order,
|
|
6
|
+
ipv6 enable). The fifth sub-app (rdns) was removed from the CLI
|
|
7
|
+
surface in 7.7 (WAF-blocked endpoint); SDK-level tests for
|
|
8
|
+
`vps.rdns.get/set/delete` stay in
|
|
9
|
+
sdk-python/tests/test_vps_specific_unit.py.
|
|
7
10
|
|
|
8
11
|
For each verb:
|
|
9
12
|
|
|
10
13
|
* happy-path test pins the URL + body
|
|
11
14
|
* a Proxmox-backend test asserts the friendly "Cloud-only" stderr
|
|
12
15
|
line (the inverse of 3.4's BackendNotSupported handling)
|
|
13
|
-
* destructive verbs (delete, restore
|
|
14
|
-
decline-at-prompt test
|
|
16
|
+
* destructive verbs (delete, restore) get a decline-at-prompt test
|
|
15
17
|
* boot-order has a client-side validation test (unknown --order
|
|
16
18
|
rejected before any HTTP)
|
|
17
19
|
"""
|
|
@@ -299,56 +301,11 @@ def test_iso_unmount(seeded_config: Path) -> None:
|
|
|
299
301
|
# ══════════════════════════════════════════════════════════════════════
|
|
300
302
|
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
route = respx.get(f"{BASE}/vps/cloud/rdns/1.2.3.4").mock(
|
|
308
|
-
return_value=httpx.Response(
|
|
309
|
-
200, json=_ok({"ip": "1.2.3.4", "hostname": "mail.example.com"})
|
|
310
|
-
)
|
|
311
|
-
)
|
|
312
|
-
result = runner.invoke(
|
|
313
|
-
app, ["vps", "cloud", "rdns", "get", "17987", "1.2.3.4"]
|
|
314
|
-
)
|
|
315
|
-
assert result.exit_code == 0, result.stderr
|
|
316
|
-
assert route.called
|
|
317
|
-
assert "mail.example.com" in result.stdout
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
@respx.mock
|
|
321
|
-
def test_rdns_set(seeded_config: Path) -> None:
|
|
322
|
-
respx.get(f"{BASE}/account/services/17987").mock(
|
|
323
|
-
return_value=httpx.Response(200, json=_service_envelope(_cloud_service()))
|
|
324
|
-
)
|
|
325
|
-
route = respx.put(f"{BASE}/vps/cloud/rdns/1.2.3.4").mock(
|
|
326
|
-
return_value=httpx.Response(200, json=_ok())
|
|
327
|
-
)
|
|
328
|
-
result = runner.invoke(
|
|
329
|
-
app,
|
|
330
|
-
["vps", "cloud", "rdns", "set", "17987", "1.2.3.4", "mail.example.com"],
|
|
331
|
-
)
|
|
332
|
-
assert result.exit_code == 0, result.stderr
|
|
333
|
-
assert route.called
|
|
334
|
-
body = json.loads(route.calls.last.request.content)
|
|
335
|
-
assert body == {"hostname": "mail.example.com"}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
@respx.mock
|
|
339
|
-
def test_rdns_delete_with_yes(seeded_config: Path) -> None:
|
|
340
|
-
respx.get(f"{BASE}/account/services/17987").mock(
|
|
341
|
-
return_value=httpx.Response(200, json=_service_envelope(_cloud_service()))
|
|
342
|
-
)
|
|
343
|
-
route = respx.delete(f"{BASE}/vps/cloud/rdns/1.2.3.4").mock(
|
|
344
|
-
return_value=httpx.Response(200, json=_ok())
|
|
345
|
-
)
|
|
346
|
-
result = runner.invoke(
|
|
347
|
-
app,
|
|
348
|
-
["vps", "cloud", "rdns", "delete", "17987", "1.2.3.4", "--yes"],
|
|
349
|
-
)
|
|
350
|
-
assert result.exit_code == 0, result.stderr
|
|
351
|
-
assert route.called
|
|
304
|
+
# rdns CLI tests removed in 7.7. The `vps cloud rdns` sub-app is no
|
|
305
|
+
# longer registered (WAF-blocked endpoint); SDK-level tests for
|
|
306
|
+
# `vps.rdns.get/set/delete` still live in
|
|
307
|
+
# sdk-python/tests/test_vps_specific_unit.py for library consumers
|
|
308
|
+
# who route around the CLI surface.
|
|
352
309
|
|
|
353
310
|
|
|
354
311
|
# ══════════════════════════════════════════════════════════════════════
|
|
@@ -1,14 +0,0 @@
|
|
|
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"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|