ibee-cli 0.4.0__tar.gz → 0.4.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.
- {ibee_cli-0.4.0/src/ibee_cli.egg-info → ibee_cli-0.4.2}/PKG-INFO +17 -21
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/README.md +15 -19
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/pyproject.toml +2 -2
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/__init__.py +1 -1
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/block_storage.py +1 -6
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/buckets.py +4 -4
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/cdn.py +4 -4
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/gpus.py +2 -2
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/load_balancers.py +3 -3
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/networking.py +2 -2
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/reserved_ips.py +12 -20
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/secrets.py +4 -6
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/vm_lifecycle.py +4 -4
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/vms.py +3 -3
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/helpers.py +6 -50
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/main.py +2 -2
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/render.py +1 -1
- {ibee_cli-0.4.0 → ibee_cli-0.4.2/src/ibee_cli.egg-info}/PKG-INFO +17 -21
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/requires.txt +1 -1
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_billing_preflight.py +73 -4
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_block_storage_cdn_commands.py +4 -5
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_cli.py +1 -1
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_foundations.py +41 -13
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_networking_parity_commands.py +11 -10
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_object_storage_commands.py +1 -1
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_review_fixes.py +4 -3
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_secret_store_commands.py +6 -6
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_vm_parity_commands.py +3 -3
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/LICENSE +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/setup.cfg +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/__init__.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/billing.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/compute.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/console.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/firewalls.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/ops.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/context.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/SOURCES.txt +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/dependency_links.txt +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/entry_points.txt +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/top_level.txt +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_cloud_vm_commands.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_networking_commands.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_parity_alignment.py +0 -0
- {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_vm_lifecycle_commands.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ibee-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Official command-line interface for the IBEE Solutions cloud platform
|
|
5
5
|
Author-email: IBEE Solutions <support@ibee.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -13,7 +13,7 @@ Classifier: Environment :: Console
|
|
|
13
13
|
Requires-Python: >=3.10
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
|
-
Requires-Dist: ibee<0.5.0,>=0.4.
|
|
16
|
+
Requires-Dist: ibee<0.5.0,>=0.4.2
|
|
17
17
|
Requires-Dist: httpx>=0.27
|
|
18
18
|
Requires-Dist: typer>=0.12
|
|
19
19
|
Requires-Dist: click>=8.1
|
|
@@ -305,15 +305,15 @@ an interactive hidden prompt, or pipe one line to `--password-stdin`.
|
|
|
305
305
|
Bucket and VM placement are automatic when `--site-id` is omitted. Use
|
|
306
306
|
`ibee compute sites` and pass `--site-id` only when placement must be pinned.
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
All product writes reach the upstream API for fresh Billing and lifecycle decisions.
|
|
309
|
+
The CLI never queries billing eligibility or calculates an admission estimate automatically.
|
|
310
|
+
Legacy `--check-billing`, `--preflight-billing`, NAT `--preflight`,
|
|
311
|
+
`--billing-check/--no-billing-check`, and `IBEE_CHECK_BILLING` are deprecated no-ops.
|
|
312
|
+
Structural, workspace, state, scope and destructive-action checks remain in place.
|
|
313
|
+
`ibee billing eligibility` is an explicit diagnostic query: `allowed: false` is data (exit 0);
|
|
314
|
+
the explicit `--require` option retains exit 1 on denial.
|
|
315
|
+
It supports `REVOKE_CREDENTIAL` and `SECURITY_RECOVERY`.
|
|
316
|
+
Upstream errors retain their classification; suspension messages do not infer who suspended an organization.
|
|
317
317
|
|
|
318
318
|
Load balancers take backends, routing, policy, health checks and L7 rules either
|
|
319
319
|
as flags (`--backend TYPE:TARGET:PORT[:WEIGHT][:tls]`, `--rule
|
|
@@ -406,7 +406,7 @@ on) controls the read-only checks that need the current state.
|
|
|
406
406
|
- **NAT gateways** exist only in `nat_gateway` VPCs (one per VPC; `nat create`
|
|
407
407
|
prints the existing one instead of sending a create). A Reserved IP must be in
|
|
408
408
|
the VPC's site, unattached and `reserved`. `--preflight` (or `--check-billing`)
|
|
409
|
-
|
|
409
|
+
is a deprecated no-op; upstream decides admission. `nat delete` shows how many NAT VMs lose
|
|
410
410
|
outbound internet and how many forwarding rules are deleted before it asks;
|
|
411
411
|
`--ip-action reserve` of a platform-assigned IP needs `--billing-catalog` (the
|
|
412
412
|
RESERVED-IP SKU); deletion is refused while a virtual IP holds a Reserved IP.
|
|
@@ -422,8 +422,8 @@ on) controls the read-only checks that need the current state.
|
|
|
422
422
|
253 characters (`--reverse-dns ""` clears it); `release` is refused while
|
|
423
423
|
attached; `attach` is for unattached IPs (`move` for an IP on another VM,
|
|
424
424
|
`--detach-from-service` for one on a NAT gateway or virtual IP); an attach to a VM
|
|
425
|
-
outside a VPC points at `reserved-ips convert`.
|
|
426
|
-
|
|
425
|
+
outside a VPC points at `reserved-ips convert`. The API decides admission for
|
|
426
|
+
`convert`.
|
|
427
427
|
- **Firewalls**: group names are unique in any case (1-120 characters) and
|
|
428
428
|
customers cannot create default groups. Rules: tcp and udp need `--port` (22 or
|
|
429
429
|
8000-8080), icmp and any take none; sources are IPv4 addresses or CIDRs (a bare
|
|
@@ -488,8 +488,7 @@ for confirmation, the read-only checks run first (`--no-check-state` skips them)
|
|
|
488
488
|
timeout). `purge` needs `--mode`; `--mode all` asks first; only the selector for the
|
|
489
489
|
mode is accepted (`--path` 1-30, https URLs only; `--hostname`, `--tag`, `--prefix`
|
|
490
490
|
1-100). A purge the CDN reports as failed exits 1.
|
|
491
|
-
- `--check-billing`
|
|
492
|
-
S3 credential create and CUSTOMDO-STD before a custom domain.
|
|
491
|
+
- `--check-billing` and `--preflight-billing` are deprecated no-ops for storage and CDN.
|
|
493
492
|
- `block-storage create --vm-type`/`--delete-on-termination`, the Block Storage delete
|
|
494
493
|
idempotency key, `cdn cache-policies` and `cdn metrics` are not yet part of the
|
|
495
494
|
published API contract; behaviour may change. Block Storage plans, bucket emptying,
|
|
@@ -520,10 +519,7 @@ inputs first.
|
|
|
520
519
|
values are refused. In `patch-value`, a JSON `null` deletes that key. `--cas` is an
|
|
521
520
|
integer >= 0; a check-and-set mismatch is reported as a conflict and never retried.
|
|
522
521
|
`list` sends a trimmed `--query` (at most 128 characters) only when it is not blank.
|
|
523
|
-
- **Billing**:
|
|
524
|
-
first, as the portal does (`--no-billing-check` turns it off; the global
|
|
525
|
-
`--check-billing` forces it on). When the token lacks `billing.read` the check is
|
|
526
|
-
skipped with a warning, because the API still enforces billing on the create.
|
|
522
|
+
- **Billing**: store and secret writes use upstream admission. Both legacy billing-check flags are no-ops.
|
|
527
523
|
- **Batch create**: every item follows the `secrets create` rules and the error names
|
|
528
524
|
its index. Duplicate names are reported (the API skips them). A file with more than
|
|
529
525
|
500 secrets or over 64 KiB is sent as consecutive requests and the results are
|
|
@@ -564,7 +560,7 @@ inputs first.
|
|
|
564
560
|
| `-o`, `--output` | `IBEE_OUTPUT` | `table`, `json`, `yaml` or `id` |
|
|
565
561
|
| `--json` | | Same as `-o json` |
|
|
566
562
|
| `--yes`, `-y` | `IBEE_ASSUME_YES=1` | Answer yes to every confirmation |
|
|
567
|
-
| `--check-billing` | `IBEE_CHECK_BILLING=1` |
|
|
563
|
+
| `--check-billing` | `IBEE_CHECK_BILLING=1` | Deprecated no-op; upstream decides admission |
|
|
568
564
|
|
|
569
565
|
Without `-o`, each command keeps its usual format (tables for most lists, JSON
|
|
570
566
|
for single resources and operation results). `-o yaml` prints YAML and `-o id`
|
|
@@ -280,15 +280,15 @@ an interactive hidden prompt, or pipe one line to `--password-stdin`.
|
|
|
280
280
|
Bucket and VM placement are automatic when `--site-id` is omitted. Use
|
|
281
281
|
`ibee compute sites` and pass `--site-id` only when placement must be pinned.
|
|
282
282
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
283
|
+
All product writes reach the upstream API for fresh Billing and lifecycle decisions.
|
|
284
|
+
The CLI never queries billing eligibility or calculates an admission estimate automatically.
|
|
285
|
+
Legacy `--check-billing`, `--preflight-billing`, NAT `--preflight`,
|
|
286
|
+
`--billing-check/--no-billing-check`, and `IBEE_CHECK_BILLING` are deprecated no-ops.
|
|
287
|
+
Structural, workspace, state, scope and destructive-action checks remain in place.
|
|
288
|
+
`ibee billing eligibility` is an explicit diagnostic query: `allowed: false` is data (exit 0);
|
|
289
|
+
the explicit `--require` option retains exit 1 on denial.
|
|
290
|
+
It supports `REVOKE_CREDENTIAL` and `SECURITY_RECOVERY`.
|
|
291
|
+
Upstream errors retain their classification; suspension messages do not infer who suspended an organization.
|
|
292
292
|
|
|
293
293
|
Load balancers take backends, routing, policy, health checks and L7 rules either
|
|
294
294
|
as flags (`--backend TYPE:TARGET:PORT[:WEIGHT][:tls]`, `--rule
|
|
@@ -381,7 +381,7 @@ on) controls the read-only checks that need the current state.
|
|
|
381
381
|
- **NAT gateways** exist only in `nat_gateway` VPCs (one per VPC; `nat create`
|
|
382
382
|
prints the existing one instead of sending a create). A Reserved IP must be in
|
|
383
383
|
the VPC's site, unattached and `reserved`. `--preflight` (or `--check-billing`)
|
|
384
|
-
|
|
384
|
+
is a deprecated no-op; upstream decides admission. `nat delete` shows how many NAT VMs lose
|
|
385
385
|
outbound internet and how many forwarding rules are deleted before it asks;
|
|
386
386
|
`--ip-action reserve` of a platform-assigned IP needs `--billing-catalog` (the
|
|
387
387
|
RESERVED-IP SKU); deletion is refused while a virtual IP holds a Reserved IP.
|
|
@@ -397,8 +397,8 @@ on) controls the read-only checks that need the current state.
|
|
|
397
397
|
253 characters (`--reverse-dns ""` clears it); `release` is refused while
|
|
398
398
|
attached; `attach` is for unattached IPs (`move` for an IP on another VM,
|
|
399
399
|
`--detach-from-service` for one on a NAT gateway or virtual IP); an attach to a VM
|
|
400
|
-
outside a VPC points at `reserved-ips convert`.
|
|
401
|
-
|
|
400
|
+
outside a VPC points at `reserved-ips convert`. The API decides admission for
|
|
401
|
+
`convert`.
|
|
402
402
|
- **Firewalls**: group names are unique in any case (1-120 characters) and
|
|
403
403
|
customers cannot create default groups. Rules: tcp and udp need `--port` (22 or
|
|
404
404
|
8000-8080), icmp and any take none; sources are IPv4 addresses or CIDRs (a bare
|
|
@@ -463,8 +463,7 @@ for confirmation, the read-only checks run first (`--no-check-state` skips them)
|
|
|
463
463
|
timeout). `purge` needs `--mode`; `--mode all` asks first; only the selector for the
|
|
464
464
|
mode is accepted (`--path` 1-30, https URLs only; `--hostname`, `--tag`, `--prefix`
|
|
465
465
|
1-100). A purge the CDN reports as failed exits 1.
|
|
466
|
-
- `--check-billing`
|
|
467
|
-
S3 credential create and CUSTOMDO-STD before a custom domain.
|
|
466
|
+
- `--check-billing` and `--preflight-billing` are deprecated no-ops for storage and CDN.
|
|
468
467
|
- `block-storage create --vm-type`/`--delete-on-termination`, the Block Storage delete
|
|
469
468
|
idempotency key, `cdn cache-policies` and `cdn metrics` are not yet part of the
|
|
470
469
|
published API contract; behaviour may change. Block Storage plans, bucket emptying,
|
|
@@ -495,10 +494,7 @@ inputs first.
|
|
|
495
494
|
values are refused. In `patch-value`, a JSON `null` deletes that key. `--cas` is an
|
|
496
495
|
integer >= 0; a check-and-set mismatch is reported as a conflict and never retried.
|
|
497
496
|
`list` sends a trimmed `--query` (at most 128 characters) only when it is not blank.
|
|
498
|
-
- **Billing**:
|
|
499
|
-
first, as the portal does (`--no-billing-check` turns it off; the global
|
|
500
|
-
`--check-billing` forces it on). When the token lacks `billing.read` the check is
|
|
501
|
-
skipped with a warning, because the API still enforces billing on the create.
|
|
497
|
+
- **Billing**: store and secret writes use upstream admission. Both legacy billing-check flags are no-ops.
|
|
502
498
|
- **Batch create**: every item follows the `secrets create` rules and the error names
|
|
503
499
|
its index. Duplicate names are reported (the API skips them). A file with more than
|
|
504
500
|
500 secrets or over 64 KiB is sent as consecutive requests and the results are
|
|
@@ -539,7 +535,7 @@ inputs first.
|
|
|
539
535
|
| `-o`, `--output` | `IBEE_OUTPUT` | `table`, `json`, `yaml` or `id` |
|
|
540
536
|
| `--json` | | Same as `-o json` |
|
|
541
537
|
| `--yes`, `-y` | `IBEE_ASSUME_YES=1` | Answer yes to every confirmation |
|
|
542
|
-
| `--check-billing` | `IBEE_CHECK_BILLING=1` |
|
|
538
|
+
| `--check-billing` | `IBEE_CHECK_BILLING=1` | Deprecated no-op; upstream decides admission |
|
|
543
539
|
|
|
544
540
|
Without `-o`, each command keeps its usual format (tables for most lists, JSON
|
|
545
541
|
for single resources and operation results). `-o yaml` prints YAML and `-o id`
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ibee-cli"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
description = "Official command-line interface for the IBEE Solutions cloud platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
"Environment :: Console",
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"ibee>=0.4.
|
|
21
|
+
"ibee>=0.4.2,<0.5.0",
|
|
22
22
|
"httpx>=0.27",
|
|
23
23
|
"typer>=0.12",
|
|
24
24
|
"click>=8.1",
|
|
@@ -47,7 +47,6 @@ from ..helpers import (
|
|
|
47
47
|
idempotency_key_option,
|
|
48
48
|
load_json_input,
|
|
49
49
|
poll_interval_option,
|
|
50
|
-
preflight_billing,
|
|
51
50
|
resolve_idempotency_key,
|
|
52
51
|
resolve_wait,
|
|
53
52
|
timeout_option,
|
|
@@ -264,7 +263,7 @@ def create_volume(
|
|
|
264
263
|
|
|
265
264
|
settings, workspace, client = _session(ctx)
|
|
266
265
|
key = resolve_idempotency_key(idempotency_key, "block-create", name)
|
|
267
|
-
# Validate first (exit 2) so a bad request never reaches the
|
|
266
|
+
# Validate first (exit 2) so a bad request never reaches the API.
|
|
268
267
|
body = build_block_volume_create_body(
|
|
269
268
|
name=name,
|
|
270
269
|
size_gb=size_gb,
|
|
@@ -277,10 +276,6 @@ def create_volume(
|
|
|
277
276
|
vm_type=vm_type,
|
|
278
277
|
delete_on_termination=delete_on_termination,
|
|
279
278
|
)
|
|
280
|
-
if settings.check_billing:
|
|
281
|
-
preflight_billing(
|
|
282
|
-
settings, client, workspace, sku_code=body.get("sku_code"), resource_type="block_storage"
|
|
283
|
-
)
|
|
284
279
|
try:
|
|
285
280
|
with api_hints({400: CREATE_PLAN_HINT}):
|
|
286
281
|
result = client.block_storage.create_block_volume(
|
|
@@ -176,7 +176,7 @@ def create_bucket(
|
|
|
176
176
|
preflight: bool = typer.Option(
|
|
177
177
|
False,
|
|
178
178
|
"--preflight-billing",
|
|
179
|
-
help="
|
|
179
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
180
180
|
),
|
|
181
181
|
) -> None:
|
|
182
182
|
"""Create a bucket."""
|
|
@@ -191,7 +191,7 @@ def create_bucket(
|
|
|
191
191
|
object_lock_enabled=bucket_lock,
|
|
192
192
|
default_retention=retention,
|
|
193
193
|
tags=tag or None,
|
|
194
|
-
preflight_billing=
|
|
194
|
+
preflight_billing=False,
|
|
195
195
|
)
|
|
196
196
|
print_json(result, id_field="name")
|
|
197
197
|
|
|
@@ -353,7 +353,7 @@ def create_credential(
|
|
|
353
353
|
preflight: bool = typer.Option(
|
|
354
354
|
False,
|
|
355
355
|
"--preflight-billing",
|
|
356
|
-
help="
|
|
356
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
357
357
|
),
|
|
358
358
|
) -> None:
|
|
359
359
|
"""Create an S3 access key; its secret is displayed only once.
|
|
@@ -368,7 +368,7 @@ def create_credential(
|
|
|
368
368
|
permission_type=permission_type,
|
|
369
369
|
bucket_scope=bucket_scope,
|
|
370
370
|
allowed_buckets=allowed_bucket or None,
|
|
371
|
-
preflight_billing=
|
|
371
|
+
preflight_billing=False,
|
|
372
372
|
)
|
|
373
373
|
# Never print only the ID: the one-time secret would be lost. -o id falls back to JSON.
|
|
374
374
|
print_structured(result, "yaml" if settings.output == "yaml" else "json", id_field="access_key_id")
|
|
@@ -152,7 +152,7 @@ def create_distribution(
|
|
|
152
152
|
preflight: bool = typer.Option(
|
|
153
153
|
False,
|
|
154
154
|
"--preflight-billing",
|
|
155
|
-
help="
|
|
155
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
156
156
|
),
|
|
157
157
|
) -> None:
|
|
158
158
|
"""Create a CDN distribution for a public bucket.
|
|
@@ -171,7 +171,7 @@ def create_distribution(
|
|
|
171
171
|
origin_type=origin_type,
|
|
172
172
|
cache_policy=cache_policy,
|
|
173
173
|
check_origin_public=check_origin,
|
|
174
|
-
preflight_billing=
|
|
174
|
+
preflight_billing=False,
|
|
175
175
|
))
|
|
176
176
|
|
|
177
177
|
|
|
@@ -327,7 +327,7 @@ def create_domain(
|
|
|
327
327
|
preflight: bool = typer.Option(
|
|
328
328
|
False,
|
|
329
329
|
"--preflight-billing",
|
|
330
|
-
help="
|
|
330
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
331
331
|
),
|
|
332
332
|
) -> None:
|
|
333
333
|
"""Add a custom domain (billed), then print the CNAME record to create."""
|
|
@@ -337,7 +337,7 @@ def create_domain(
|
|
|
337
337
|
distribution_id,
|
|
338
338
|
workspace_id=workspace,
|
|
339
339
|
domain=domain,
|
|
340
|
-
preflight_billing=
|
|
340
|
+
preflight_billing=False,
|
|
341
341
|
)
|
|
342
342
|
_print(result)
|
|
343
343
|
if not settings.structured_output:
|
|
@@ -151,7 +151,7 @@ def create_gpu_vm(
|
|
|
151
151
|
preflight: bool = typer.Option(
|
|
152
152
|
False,
|
|
153
153
|
"--preflight-billing",
|
|
154
|
-
help="
|
|
154
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
155
155
|
),
|
|
156
156
|
wait: bool = typer.Option(False, "--wait", help="Poll until the VM is provisioned"),
|
|
157
157
|
timeout: Optional[float] = timeout_option(),
|
|
@@ -223,7 +223,7 @@ def delete_gpu_vm(
|
|
|
223
223
|
reserved_ip_billing_catalog_file: Optional[str] = typer.Option(None, "--reserved-ip-billing-catalog-file"),
|
|
224
224
|
requested_by: Optional[str] = typer.Option(None, "--requested-by"),
|
|
225
225
|
preflight: bool = typer.Option(
|
|
226
|
-
False, "--preflight-billing", help="
|
|
226
|
+
False, "--preflight-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
|
|
227
227
|
),
|
|
228
228
|
check_state: bool = check_state_option(),
|
|
229
229
|
wait: bool = typer.Option(False, "--wait", help="Poll until the VM is deleted"),
|
|
@@ -275,7 +275,7 @@ def _create(
|
|
|
275
275
|
) -> None:
|
|
276
276
|
settings, workspace, client = _session(ctx)
|
|
277
277
|
method = getattr(client.load_balancers, f"create_{layer}load_balancer")
|
|
278
|
-
lb = method(workspace_id=workspace, check_billing=
|
|
278
|
+
lb = method(workspace_id=workspace, check_billing=False,
|
|
279
279
|
**{key: value for key, value in fields.items() if value is not None})
|
|
280
280
|
print_json(lb, id_field=ID_FIELD)
|
|
281
281
|
|
|
@@ -333,7 +333,7 @@ def create_l4_load_balancer(
|
|
|
333
333
|
None, "--tls", help="TLS JSON object (managed certificates only; default for tls_passthrough)"
|
|
334
334
|
),
|
|
335
335
|
check_billing: bool = typer.Option(
|
|
336
|
-
False, "--check-billing", help="
|
|
336
|
+
False, "--check-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
|
|
337
337
|
),
|
|
338
338
|
) -> None:
|
|
339
339
|
"""Create an L4 load balancer (TCP or TLS passthrough)."""
|
|
@@ -402,7 +402,7 @@ def create_l7_load_balancer(
|
|
|
402
402
|
),
|
|
403
403
|
rules: Optional[str] = typer.Option(None, "--rules", help="Rules JSON array"),
|
|
404
404
|
check_billing: bool = typer.Option(
|
|
405
|
-
False, "--check-billing", help="
|
|
405
|
+
False, "--check-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
|
|
406
406
|
),
|
|
407
407
|
) -> None:
|
|
408
408
|
"""Create an L7 load balancer (HTTP or HTTPS)."""
|
|
@@ -529,7 +529,7 @@ def create_nat_gateway(
|
|
|
529
529
|
),
|
|
530
530
|
preflight: bool = typer.Option(
|
|
531
531
|
False, "--preflight", "--preflight-billing",
|
|
532
|
-
help="
|
|
532
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
533
533
|
),
|
|
534
534
|
check_state: bool = check_state_option(),
|
|
535
535
|
) -> None:
|
|
@@ -561,7 +561,7 @@ def create_nat_gateway(
|
|
|
561
561
|
reserved_public_ip_id=reserved_public_ip_id,
|
|
562
562
|
name=name,
|
|
563
563
|
billing_catalog=catalog,
|
|
564
|
-
preflight_billing=
|
|
564
|
+
preflight_billing=False,
|
|
565
565
|
check_state=False,
|
|
566
566
|
)
|
|
567
567
|
print_json(gateway, id_field="nat_gateway_id")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Python SDK applies the portal's rules: label and reverse-DNS formats, no release
|
|
4
4
|
while attached, attach versus move, no detach of a converted address that is still
|
|
5
|
-
the VM's active IP, and
|
|
5
|
+
the VM's active IP, and upstream admission for conversions.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
@@ -60,7 +60,7 @@ def reserve_ip(
|
|
|
60
60
|
None, "--billing-catalog-file", metavar="PATH", help="Read --billing-catalog from a JSON file"
|
|
61
61
|
),
|
|
62
62
|
check_billing: bool = typer.Option(
|
|
63
|
-
False, "--check-billing", help="
|
|
63
|
+
False, "--check-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
|
|
64
64
|
),
|
|
65
65
|
) -> None:
|
|
66
66
|
"""Reserve a new public IP address (billed while reserved)."""
|
|
@@ -72,7 +72,7 @@ def reserve_ip(
|
|
|
72
72
|
site_id=site_id,
|
|
73
73
|
label=label,
|
|
74
74
|
billing_catalog=catalog,
|
|
75
|
-
check_billing=
|
|
75
|
+
check_billing=False,
|
|
76
76
|
)
|
|
77
77
|
print_json(reserved, id_field=ID_FIELD)
|
|
78
78
|
|
|
@@ -93,7 +93,7 @@ def convert_vm_public_ip(
|
|
|
93
93
|
billing_check: bool = typer.Option(
|
|
94
94
|
True,
|
|
95
95
|
"--billing-check/--no-billing-check",
|
|
96
|
-
help="
|
|
96
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
97
97
|
),
|
|
98
98
|
) -> None:
|
|
99
99
|
"""Keep a VM's current public IPv4 as a Reserved IP (VMs outside a VPC).
|
|
@@ -103,22 +103,14 @@ def convert_vm_public_ip(
|
|
|
103
103
|
|
|
104
104
|
_settings, workspace, client = _session(ctx)
|
|
105
105
|
catalog = load_json_input(billing_catalog, billing_catalog_file, "billing-catalog")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
115
|
-
except ForbiddenError as exc:
|
|
116
|
-
text = str(getattr(exc, "message", "") or "")
|
|
117
|
-
if billing_check and (getattr(exc, "required_scope", None) == "billing.read" or "billing.read" in text):
|
|
118
|
-
exc.cli_hint = ( # type: ignore[attr-defined]
|
|
119
|
-
"The RESERVED-IP billing check needs the billing.read scope; grant it or pass --no-billing-check."
|
|
120
|
-
)
|
|
121
|
-
raise
|
|
106
|
+
reserved = client.reserved_ips.convert_vm_public_ip_to_reserved_ip(
|
|
107
|
+
workspace_id=workspace,
|
|
108
|
+
vm_id=vm_id,
|
|
109
|
+
site_id=site_id,
|
|
110
|
+
label=label,
|
|
111
|
+
billing_catalog=catalog,
|
|
112
|
+
billing_check=False,
|
|
113
|
+
)
|
|
122
114
|
print_json(reserved, id_field=ID_FIELD)
|
|
123
115
|
|
|
124
116
|
|
|
@@ -278,8 +278,7 @@ def create_store(
|
|
|
278
278
|
billing_check: bool = typer.Option(
|
|
279
279
|
True,
|
|
280
280
|
"--billing-check/--no-billing-check",
|
|
281
|
-
help="
|
|
282
|
-
"Skipped with a warning when the token lacks billing.read",
|
|
281
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
283
282
|
),
|
|
284
283
|
if_exists: IfExists = typer.Option(
|
|
285
284
|
IfExists.ERROR,
|
|
@@ -299,7 +298,7 @@ def create_store(
|
|
|
299
298
|
workspace_id=workspace,
|
|
300
299
|
name=body["name"],
|
|
301
300
|
description=body.get("description"),
|
|
302
|
-
preflight_billing=
|
|
301
|
+
preflight_billing=False,
|
|
303
302
|
)
|
|
304
303
|
except ConflictError as exc:
|
|
305
304
|
if if_exists is not IfExists.REUSE or type(exc) is not ConflictError:
|
|
@@ -997,8 +996,7 @@ def create_secret(
|
|
|
997
996
|
billing_check: bool = typer.Option(
|
|
998
997
|
True,
|
|
999
998
|
"--billing-check/--no-billing-check",
|
|
1000
|
-
help="
|
|
1001
|
-
"Skipped with a warning when the token lacks billing.read",
|
|
999
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
1002
1000
|
),
|
|
1003
1001
|
) -> None:
|
|
1004
1002
|
"""Create a secret in an active store."""
|
|
@@ -1016,7 +1014,7 @@ def create_secret(
|
|
|
1016
1014
|
workspace_id=workspace,
|
|
1017
1015
|
secret_name=secret_name,
|
|
1018
1016
|
value=secret_value,
|
|
1019
|
-
preflight_billing=
|
|
1017
|
+
preflight_billing=False,
|
|
1020
1018
|
)
|
|
1021
1019
|
if settings.structured_output:
|
|
1022
1020
|
print_json(result)
|
|
@@ -398,7 +398,7 @@ def create_vms(
|
|
|
398
398
|
),
|
|
399
399
|
)
|
|
400
400
|
if preflight or getattr(settings, "check_billing", False):
|
|
401
|
-
common["preflight_billing"] =
|
|
401
|
+
common["preflight_billing"] = False
|
|
402
402
|
results = []
|
|
403
403
|
for vm_name, key in zip(names, keys):
|
|
404
404
|
try:
|
|
@@ -521,7 +521,7 @@ def delete_vm(
|
|
|
521
521
|
kwargs = resolve("reserve" if reserve else "release")
|
|
522
522
|
if preflight or getattr(settings, "check_billing", False):
|
|
523
523
|
if kwargs.get("public_ip_action") == "reserve":
|
|
524
|
-
kwargs["preflight_billing"] =
|
|
524
|
+
kwargs["preflight_billing"] = False
|
|
525
525
|
result = _method(resource, spec, "delete")(
|
|
526
526
|
vm_id=vm_id,
|
|
527
527
|
workspace_id=workspace,
|
|
@@ -1041,7 +1041,7 @@ def register_vm_lifecycle(app: typer.Typer, spec: VmCommandSpec) -> None:
|
|
|
1041
1041
|
billing_catalog: Optional[str] = typer.Option(None, "--billing-catalog", help=SNAPSHOT_SKU_HELP),
|
|
1042
1042
|
billing_catalog_file: Optional[str] = typer.Option(None, "--billing-catalog-file", help="File with the SKU JSON"),
|
|
1043
1043
|
preflight: bool = typer.Option(
|
|
1044
|
-
False, "--preflight-billing", help="
|
|
1044
|
+
False, "--preflight-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
|
|
1045
1045
|
),
|
|
1046
1046
|
requested_by: Optional[str] = typer.Option(None, "--requested-by"),
|
|
1047
1047
|
check_state: bool = check_state_option(),
|
|
@@ -1065,7 +1065,7 @@ def register_vm_lifecycle(app: typer.Typer, spec: VmCommandSpec) -> None:
|
|
|
1065
1065
|
mode=mode,
|
|
1066
1066
|
selected_data_volume_ids=selected_data_volume_id,
|
|
1067
1067
|
requested_by=requested_by,
|
|
1068
|
-
preflight_billing=
|
|
1068
|
+
preflight_billing=False,
|
|
1069
1069
|
),
|
|
1070
1070
|
)
|
|
1071
1071
|
snapshot_id = cell(result, "snapshot_set_id") or cell(result, "id")
|
|
@@ -103,7 +103,7 @@ def create_vm(
|
|
|
103
103
|
ram_mb: Optional[int] = typer.Option(None, "--ram-mb", help="RAM in MB (default: the plan's; must match it)"),
|
|
104
104
|
disk_gb: Optional[int] = typer.Option(None, "--disk-gb", help="Root disk in GB (default: the plan's; must match it)"),
|
|
105
105
|
billing_term: Optional[str] = typer.Option(
|
|
106
|
-
None, "--billing-term", help="Billing term: " + ", ".join(BILLING_TERMS) + " (default HOURLY, or the plan's first term)"
|
|
106
|
+
None, "--billing-term", help="Billing term sent to the API: " + ", ".join(BILLING_TERMS) + " (default HOURLY, or the plan's first term); admission is decided upstream"
|
|
107
107
|
),
|
|
108
108
|
windows_license: Optional[str] = typer.Option(
|
|
109
109
|
None,
|
|
@@ -150,7 +150,7 @@ def create_vm(
|
|
|
150
150
|
preflight: bool = typer.Option(
|
|
151
151
|
False,
|
|
152
152
|
"--preflight-billing",
|
|
153
|
-
help="
|
|
153
|
+
help="Deprecated no-op; upstream decides billing and lifecycle admission.",
|
|
154
154
|
),
|
|
155
155
|
wait: bool = typer.Option(False, "--wait", help="Poll until the VM is provisioned"),
|
|
156
156
|
timeout: Optional[float] = timeout_option(),
|
|
@@ -222,7 +222,7 @@ def delete_vm(
|
|
|
222
222
|
reserved_ip_billing_catalog_file: Optional[str] = typer.Option(None, "--reserved-ip-billing-catalog-file"),
|
|
223
223
|
requested_by: Optional[str] = typer.Option(None, "--requested-by"),
|
|
224
224
|
preflight: bool = typer.Option(
|
|
225
|
-
False, "--preflight-billing", help="
|
|
225
|
+
False, "--preflight-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
|
|
226
226
|
),
|
|
227
227
|
check_state: bool = check_state_option(),
|
|
228
228
|
wait: bool = typer.Option(False, "--wait", help="Poll until the VM is deleted"),
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Compute writes (VM create/delete/power, access, resize, and volume actions)
|
|
4
4
|
return `202 + operation_id`; these helpers build idempotency keys, optionally
|
|
5
|
-
poll the operation to a terminal state (`--wait`),
|
|
6
|
-
|
|
5
|
+
poll the operation to a terminal state (`--wait`), retain deprecated no-op billing
|
|
6
|
+
flags, ask for confirmation of destructive actions,
|
|
7
7
|
and parse structured values from the command line.
|
|
8
8
|
"""
|
|
9
9
|
|
|
@@ -549,36 +549,8 @@ def preflight_billing(
|
|
|
549
549
|
estimated_cost_minor: Optional[int | float] = None,
|
|
550
550
|
resource_type: str = "resource",
|
|
551
551
|
) -> Any:
|
|
552
|
-
"""
|
|
553
|
-
|
|
554
|
-
Without ``--check-billing`` (or ``IBEE_CHECK_BILLING``) nothing is called, so a
|
|
555
|
-
create stays a single request and the API edge alone decides admission. With it,
|
|
556
|
-
the create continues only when billing answers ``allowed`` exactly ``true``;
|
|
557
|
-
otherwise ``BillingDeniedError`` is raised (printed with the portal wording, exit 1).
|
|
558
|
-
"""
|
|
559
|
-
|
|
560
|
-
if not getattr(settings, "check_billing", False):
|
|
561
|
-
return None
|
|
562
|
-
billing = getattr(client, "billing", None)
|
|
563
|
-
require = getattr(billing, "require_resource_eligibility", None)
|
|
564
|
-
if callable(require):
|
|
565
|
-
kwargs: dict[str, Any] = {"workspace_id": workspace_id, "resource_type": resource_type}
|
|
566
|
-
sku = normalize_sku_code(sku_code)
|
|
567
|
-
if sku:
|
|
568
|
-
kwargs["sku_code"] = sku
|
|
569
|
-
cost = normalize_estimated_cost_minor(estimated_cost_minor)
|
|
570
|
-
if cost is not None:
|
|
571
|
-
kwargs["estimated_cost_minor"] = cost
|
|
572
|
-
return require(**kwargs)
|
|
573
|
-
decision = check_billing_eligibility(
|
|
574
|
-
client,
|
|
575
|
-
workspace_id,
|
|
576
|
-
sku_code=sku_code,
|
|
577
|
-
estimated_cost_minor=estimated_cost_minor,
|
|
578
|
-
)
|
|
579
|
-
if _field(decision, "allowed") is not True:
|
|
580
|
-
raise BillingDeniedError(decision=decision, create_type=resource_type)
|
|
581
|
-
return decision
|
|
552
|
+
"""Deprecated compatibility no-op; the mutation endpoint decides admission."""
|
|
553
|
+
return None
|
|
582
554
|
|
|
583
555
|
|
|
584
556
|
def preflight_create(
|
|
@@ -589,24 +561,8 @@ def preflight_create(
|
|
|
589
561
|
sku_code: Optional[str] = None,
|
|
590
562
|
estimated_cost_minor: Optional[int | float] = None,
|
|
591
563
|
) -> Any:
|
|
592
|
-
"""
|
|
593
|
-
|
|
594
|
-
Does nothing (and sends nothing) unless ``--check-billing``/``IBEE_CHECK_BILLING`` is set.
|
|
595
|
-
"""
|
|
596
|
-
|
|
597
|
-
if not getattr(settings, "check_billing", False):
|
|
598
|
-
return None
|
|
599
|
-
from .context import get_client, require_workspace
|
|
600
|
-
|
|
601
|
-
workspace = require_workspace(settings)
|
|
602
|
-
return preflight_billing(
|
|
603
|
-
settings,
|
|
604
|
-
client if client is not None else get_client(settings),
|
|
605
|
-
workspace,
|
|
606
|
-
sku_code=sku_code,
|
|
607
|
-
estimated_cost_minor=estimated_cost_minor,
|
|
608
|
-
resource_type=resource_type,
|
|
609
|
-
)
|
|
564
|
+
"""Deprecated compatibility no-op; no client or workspace is resolved."""
|
|
565
|
+
return None
|
|
610
566
|
|
|
611
567
|
|
|
612
568
|
# ---------------------------------------------------------------------------
|
|
@@ -87,8 +87,8 @@ def main(
|
|
|
87
87
|
False,
|
|
88
88
|
"--check-billing",
|
|
89
89
|
help=(
|
|
90
|
-
"
|
|
91
|
-
"(
|
|
90
|
+
"Deprecated no-op; upstream decides mutation admission "
|
|
91
|
+
"(also applies to IBEE_CHECK_BILLING)"
|
|
92
92
|
),
|
|
93
93
|
),
|
|
94
94
|
version: bool = typer.Option(
|
|
@@ -495,7 +495,7 @@ def _api_error_lines(exc: ApiError) -> list[str]:
|
|
|
495
495
|
return [f"Validation failed (422): {message}"]
|
|
496
496
|
if status == 423 or isinstance(exc, OrganizationSuspendedError):
|
|
497
497
|
return [
|
|
498
|
-
"Organization suspended (423):
|
|
498
|
+
"Organization suspended (423): this organization is suspended, "
|
|
499
499
|
f"so this change is blocked. {message}"
|
|
500
500
|
]
|
|
501
501
|
if status == 429:
|