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.
Files changed (63) hide show
  1. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/PKG-INFO +3 -3
  2. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/README.md +2 -2
  3. impreza_cli-0.3.2/impreza_cli/__init__.py +24 -0
  4. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/vps_cloud.py +30 -123
  5. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/PKG-INFO +3 -3
  6. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_5_smoke.py +18 -68
  7. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_vps_cloud_commands.py +14 -57
  8. impreza_cli-0.3.0/impreza_cli/__init__.py +0 -14
  9. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/__init__.py +0 -0
  10. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/_helpers.py +0 -0
  11. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/account.py +0 -0
  12. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/catalog.py +0 -0
  13. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/context.py +0 -0
  14. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/doctor.py +0 -0
  15. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/domain.py +0 -0
  16. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/invoice.py +0 -0
  17. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/key.py +0 -0
  18. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/orders.py +0 -0
  19. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/services.py +0 -0
  20. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/vps.py +0 -0
  21. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/vps_proxmox.py +0 -0
  22. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/commands/webhooks.py +0 -0
  23. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/config.py +0 -0
  24. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/main.py +0 -0
  25. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/output.py +0 -0
  26. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/sdk.py +0 -0
  27. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli/state.py +0 -0
  28. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/SOURCES.txt +0 -0
  29. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/dependency_links.txt +0 -0
  30. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/entry_points.txt +0 -0
  31. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/requires.txt +0 -0
  32. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/impreza_cli.egg-info/top_level.txt +0 -0
  33. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/pyproject.toml +0 -0
  34. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/setup.cfg +0 -0
  35. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/.gitkeep +0 -0
  36. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/__init__.py +0 -0
  37. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/conftest.py +0 -0
  38. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_account_commands.py +0 -0
  39. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_account_topup_commands.py +0 -0
  40. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_catalog_commands.py +0 -0
  41. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_config.py +0 -0
  42. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_context_commands.py +0 -0
  43. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_doctor_commands.py +0 -0
  44. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_domain_commands.py +0 -0
  45. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_invoice_commands.py +0 -0
  46. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_key_commands.py +0 -0
  47. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_orders_commands.py +0 -0
  48. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_2_smoke.py +0 -0
  49. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_3_smoke.py +0 -0
  50. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_4_smoke.py +0 -0
  51. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_5_smoke.py +0 -0
  52. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_2_6_smoke.py +0 -0
  53. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_1_smoke.py +0 -0
  54. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_2_smoke.py +0 -0
  55. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_3_smoke.py +0 -0
  56. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_4_smoke.py +0 -0
  57. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_6_smoke.py +0 -0
  58. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_phase_3_7_smoke.py +0 -0
  59. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_services_commands.py +0 -0
  60. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_vps_commands.py +0 -0
  61. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_vps_proxmox_commands.py +0 -0
  62. {impreza_cli-0.3.0 → impreza_cli-0.3.2}/tests/test_webhooks_commands.py +0 -0
  63. {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.0
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 / rdns / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
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 / rdns / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
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
- Five nested sub-apps + five inline verbs at the cloud root:
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, rdns, ssh-keys, etc.",
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
- @rdns_app.command("get")
456
- def rdns_get(
457
- typer_ctx: typer.Context,
458
- service_id: int = typer.Argument(
459
- ...,
460
- help=(
461
- "Service id of any Cloud VPS on the account (rDNS is "
462
- "account-scoped; this id is only used to confirm the account)."
463
- ),
464
- ),
465
- ip: str = typer.Argument(..., help="IP address whose rDNS to read."),
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.0
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 / rdns / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
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
- rdns set/delete, ipv6 enable): real state changes — some
12
- reversible (boot-order, rescue, iso) and some that consume
13
- storage quota (images create) or have billing impact
14
- (resize). Mock-only by design.
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 equivalents here:
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
- # ── rdns: read for the VPS's own dedicated IP ───────────────────────
179
-
180
-
181
- @pytest.mark.skipif(
182
- os.environ.get("IMPREZA_DESTRUCTIVE_TESTS") != "1",
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 five nested
4
- sub-apps (images, rescue, iso, rdns, ssh-keys) plus the five
5
- inline verbs at the cloud root (vnc, vnc-password, resize,
6
- boot-order, ipv6 enable).
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, rdns delete) get a
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
- @respx.mock
303
- def test_rdns_get(seeded_config: Path) -> None:
304
- respx.get(f"{BASE}/account/services/17987").mock(
305
- return_value=httpx.Response(200, json=_service_envelope(_cloud_service()))
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