cgcsdk 1.2.6__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cgc/.env +2 -2
- cgc/CHANGELOG.md +10 -0
- cgc/commands/compute/billing/billing_cmd.py +33 -74
- cgc/commands/compute/billing/billing_responses.py +34 -24
- cgc/commands/compute/billing/billing_utils.py +137 -59
- cgc/commands/volume/volume_responses.py +1 -1
- cgc/utils/custom_exceptions.py +26 -26
- cgc/utils/message_utils.py +4 -0
- cgc/utils/prepare_headers.py +3 -3
- cgc/utils/requests_helper.py +4 -0
- {cgcsdk-1.2.6.dist-info → cgcsdk-1.3.0.dist-info}/METADATA +1 -1
- {cgcsdk-1.2.6.dist-info → cgcsdk-1.3.0.dist-info}/RECORD +16 -16
- {cgcsdk-1.2.6.dist-info → cgcsdk-1.3.0.dist-info}/WHEEL +1 -1
- {cgcsdk-1.2.6.dist-info → cgcsdk-1.3.0.dist-info}/entry_points.txt +0 -0
- {cgcsdk-1.2.6.dist-info → cgcsdk-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {cgcsdk-1.2.6.dist-info → cgcsdk-1.3.0.dist-info}/top_level.txt +0 -0
cgc/.env
CHANGED
cgc/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.3.0
|
4
|
+
|
5
|
+
Release on July 08, 2025
|
6
|
+
|
7
|
+
* stop events are not longer supported
|
8
|
+
* billing endpoints v1 are not longer supported
|
9
|
+
* added support for v2 billing endpoints
|
10
|
+
* added `cgc billing pricing` command
|
11
|
+
* error messages are now more descriptive
|
12
|
+
|
3
13
|
## 1.2.6
|
4
14
|
|
5
15
|
Release on Apr 30, 2025
|
@@ -2,12 +2,10 @@ import click
|
|
2
2
|
|
3
3
|
from datetime import datetime
|
4
4
|
|
5
|
-
from cgc.commands.compute.billing.billing_utils import verify_input_datetime
|
6
5
|
from cgc.commands.compute.billing.billing_responses import (
|
6
|
+
billing_pricing_response,
|
7
7
|
billing_status_response,
|
8
8
|
billing_invoice_response,
|
9
|
-
stop_events_resource_response,
|
10
|
-
stop_events_volume_response,
|
11
9
|
)
|
12
10
|
from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
|
13
11
|
from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
|
@@ -24,12 +22,22 @@ def billing_group():
|
|
24
22
|
|
25
23
|
|
26
24
|
@billing_group.command("status", cls=CustomCommand)
|
27
|
-
|
25
|
+
@click.option(
|
26
|
+
"--detailed",
|
27
|
+
"-d",
|
28
|
+
"detailed",
|
29
|
+
prompt=True,
|
30
|
+
type=bool,
|
31
|
+
default=False,
|
32
|
+
help="If true, returns detailed invoice information",
|
33
|
+
is_flag=True,
|
34
|
+
)
|
35
|
+
def billing_status(detailed: bool):
|
28
36
|
"""
|
29
37
|
Shows billing status for user namespace
|
30
38
|
"""
|
31
39
|
api_url, headers = get_api_url_and_prepare_headers()
|
32
|
-
url = f"{api_url}/
|
40
|
+
url = f"{api_url}/v2/api/billing/status?details={detailed}"
|
33
41
|
metric = "billing.status"
|
34
42
|
__res = call_api(request=EndpointTypes.get, url=url, headers=headers)
|
35
43
|
click.echo(
|
@@ -64,12 +72,24 @@ def _get_previous_year_if_required():
|
|
64
72
|
type=click.IntRange(1, 12),
|
65
73
|
default=_get_previous_month(),
|
66
74
|
)
|
67
|
-
|
75
|
+
@click.option(
|
76
|
+
"--detailed",
|
77
|
+
"-d",
|
78
|
+
"detailed",
|
79
|
+
prompt=True,
|
80
|
+
type=bool,
|
81
|
+
default=False,
|
82
|
+
help="If true, returns detailed invoice information",
|
83
|
+
is_flag=True,
|
84
|
+
)
|
85
|
+
def billing_invoice(year: int, month: int, detailed: bool):
|
68
86
|
"""
|
69
87
|
Opens invoice from given year and month
|
70
88
|
"""
|
71
89
|
api_url, headers = get_api_url_and_prepare_headers()
|
72
|
-
url =
|
90
|
+
url = (
|
91
|
+
f"{api_url}/v2/api/billing/invoice?year={year}&month={month}&details={detailed}"
|
92
|
+
)
|
73
93
|
metric = "billing.invoice"
|
74
94
|
__res = call_api(request=EndpointTypes.get, url=url, headers=headers)
|
75
95
|
|
@@ -82,78 +102,17 @@ def billing_invoice(year: int, month: int):
|
|
82
102
|
)
|
83
103
|
|
84
104
|
|
85
|
-
@
|
86
|
-
def
|
87
|
-
"""
|
88
|
-
List stop events information.
|
89
|
-
"""
|
90
|
-
pass
|
91
|
-
|
92
|
-
|
93
|
-
@stop_events_group.command("resource")
|
94
|
-
@click.option(
|
95
|
-
"--date_from",
|
96
|
-
"-f",
|
97
|
-
"date_from",
|
98
|
-
prompt="Date from (DD-MM-YYYY)",
|
99
|
-
default=datetime.now().replace(day=1).strftime("%d-%m-%Y"),
|
100
|
-
help="Start date for filtering stop events",
|
101
|
-
)
|
102
|
-
@click.option(
|
103
|
-
"--date_to",
|
104
|
-
"-t",
|
105
|
-
"date_to",
|
106
|
-
prompt="Date to (DD-MM-YYYY)",
|
107
|
-
default=datetime.now().strftime("%d-%m-%Y"),
|
108
|
-
help="End date for filtering stop events",
|
109
|
-
)
|
110
|
-
def stop_events_resource(date_from, date_to):
|
111
|
-
"""
|
112
|
-
List resource stop events information for a given time period
|
113
|
-
"""
|
114
|
-
verify_input_datetime(date_from, date_to)
|
115
|
-
api_url, headers = get_api_url_and_prepare_headers()
|
116
|
-
url = f"{api_url}/v1/api/billing/list_resource_stop_events?time_from={date_from}&time_till={date_to}"
|
117
|
-
metric = "billing.stop_events.resource"
|
118
|
-
__res = call_api(request=EndpointTypes.get, url=url, headers=headers)
|
119
|
-
click.echo(
|
120
|
-
stop_events_resource_response(
|
121
|
-
retrieve_and_validate_response_send_metric(__res, metric)
|
122
|
-
)
|
123
|
-
)
|
124
|
-
|
125
|
-
|
126
|
-
@stop_events_group.command("volume")
|
127
|
-
@click.option(
|
128
|
-
"--date_from",
|
129
|
-
"-f",
|
130
|
-
"date_from",
|
131
|
-
prompt="Date from (DD-MM-YYYY)",
|
132
|
-
default=datetime.now().replace(day=1).strftime("%d-%m-%Y"),
|
133
|
-
help="Start date for filtering stop events",
|
134
|
-
)
|
135
|
-
@click.option(
|
136
|
-
"--date_to",
|
137
|
-
"-t",
|
138
|
-
"date_to",
|
139
|
-
prompt="Date to (DD-MM-YYYY)",
|
140
|
-
default=datetime.now().strftime("%d-%m-%Y"),
|
141
|
-
help="End date for filtering stop events",
|
142
|
-
)
|
143
|
-
def stop_events_volume(date_from, date_to):
|
105
|
+
@billing_group.command("pricing", cls=CustomCommand)
|
106
|
+
def billing_pricing():
|
144
107
|
"""
|
145
|
-
|
108
|
+
Shows billing pricing information for user
|
146
109
|
"""
|
147
|
-
verify_input_datetime(date_from, date_to)
|
148
110
|
api_url, headers = get_api_url_and_prepare_headers()
|
149
|
-
url = f"{api_url}/
|
150
|
-
metric = "billing.
|
111
|
+
url = f"{api_url}/v2/api/billing/user_pricing"
|
112
|
+
metric = "billing.pricing"
|
151
113
|
__res = call_api(request=EndpointTypes.get, url=url, headers=headers)
|
152
114
|
click.echo(
|
153
|
-
|
115
|
+
billing_pricing_response(
|
154
116
|
retrieve_and_validate_response_send_metric(__res, metric)
|
155
117
|
)
|
156
118
|
)
|
157
|
-
|
158
|
-
|
159
|
-
billing_group.add_command(stop_events_group)
|
@@ -1,15 +1,12 @@
|
|
1
1
|
import calendar
|
2
|
+
import click
|
3
|
+
from decimal import Decimal
|
4
|
+
from tabulate import tabulate
|
2
5
|
from cgc.commands.compute.billing import (
|
3
6
|
NoCostsFound,
|
4
7
|
NoInvoiceFoundForSelectedMonth,
|
5
|
-
NoResourceStopEvents,
|
6
|
-
NoVolumeStopEvents,
|
7
|
-
)
|
8
|
-
from cgc.commands.compute.billing.billing_utils import (
|
9
|
-
get_billing_status_message,
|
10
|
-
get_table_compute_stop_events_message,
|
11
|
-
get_table_volume_stop_events_message,
|
12
8
|
)
|
9
|
+
from cgc.commands.compute.billing.billing_utils import get_billing_status_message
|
13
10
|
from cgc.utils.message_utils import key_error_decorator_for_helpers
|
14
11
|
|
15
12
|
|
@@ -17,10 +14,11 @@ from cgc.utils.message_utils import key_error_decorator_for_helpers
|
|
17
14
|
def billing_status_response(data: dict) -> str:
|
18
15
|
total_cost = data["details"]["cost_total"]
|
19
16
|
namespace = data["details"]["namespace"]
|
20
|
-
|
21
|
-
|
17
|
+
billing_records = data["details"]["billing_records"]
|
18
|
+
details = data["details"].get("details", [])
|
19
|
+
if not billing_records:
|
22
20
|
raise NoCostsFound()
|
23
|
-
message = get_billing_status_message(
|
21
|
+
message = get_billing_status_message(billing_records, details)
|
24
22
|
message += f"Total cost for namespace {namespace}: {total_cost:.2f} pln"
|
25
23
|
return message
|
26
24
|
|
@@ -29,27 +27,39 @@ def billing_status_response(data: dict) -> str:
|
|
29
27
|
def billing_invoice_response(year: int, month: int, data: dict) -> str:
|
30
28
|
total_cost = float(data["details"]["cost_total"])
|
31
29
|
namespace = data["details"]["namespace"]
|
32
|
-
|
30
|
+
billing_records = data["details"]["billing_records"]
|
31
|
+
details = data["details"].get("details", [])
|
33
32
|
if (
|
34
|
-
not
|
33
|
+
not billing_records or total_cost == 0
|
35
34
|
): # TODO: total_cost == 0 is it correct thinking?
|
36
35
|
raise NoInvoiceFoundForSelectedMonth(year, month)
|
37
|
-
message = get_billing_status_message(
|
36
|
+
message = get_billing_status_message(billing_records, details)
|
38
37
|
message += f"Total cost for namespace {namespace} in {calendar.month_name[month]} {year}: {total_cost:.2f} pln"
|
39
38
|
return message
|
40
39
|
|
41
40
|
|
42
41
|
@key_error_decorator_for_helpers
|
43
|
-
def
|
44
|
-
|
45
|
-
if not event_list:
|
46
|
-
raise NoResourceStopEvents()
|
47
|
-
return get_table_compute_stop_events_message(event_list)
|
42
|
+
def billing_pricing_response(data: dict) -> str:
|
43
|
+
"""Create response string for billing pricing command.
|
48
44
|
|
45
|
+
:return: Response string.
|
46
|
+
:rtype: str
|
47
|
+
"""
|
48
|
+
pricing_details = data["details"]["pricing_details"]
|
49
|
+
if not pricing_details:
|
50
|
+
return "No pricing details available."
|
51
|
+
if pricing_details.get("tier"):
|
52
|
+
tier = pricing_details["tier"] or "DEFAULT"
|
53
|
+
click.echo(f"Current pricing tier: {tier}")
|
54
|
+
if not pricing_details.get("resources"):
|
55
|
+
return "No resources costs available."
|
56
|
+
headers = ["Resource", "Price per unit (pln) / (second OR token)"]
|
57
|
+
pricing_data = [
|
58
|
+
(resource, f"{Decimal(str(price)):.7f}")
|
59
|
+
for resource, price in pricing_details.get("resources").items()
|
60
|
+
]
|
61
|
+
click.echo(
|
62
|
+
"Pricing values displayed are approximated due to float representation. For exact values, refer to the billing system dashboard."
|
63
|
+
)
|
49
64
|
|
50
|
-
|
51
|
-
def stop_events_volume_response(data: dict) -> str:
|
52
|
-
event_list = data["details"]["event_list"]
|
53
|
-
if not event_list:
|
54
|
-
raise NoVolumeStopEvents()
|
55
|
-
return get_table_volume_stop_events_message(event_list)
|
65
|
+
return tabulate(pricing_data, headers=headers)
|
@@ -17,7 +17,7 @@ def verify_input_datetime(*args):
|
|
17
17
|
|
18
18
|
|
19
19
|
def _get_costs_list_for_user(costs_list: list):
|
20
|
-
"""
|
20
|
+
"""Format data in costs list to be displayed in a table and calculate user cost
|
21
21
|
|
22
22
|
:param costs_list: list of costs for user
|
23
23
|
:type costs_list: list
|
@@ -25,83 +25,142 @@ def _get_costs_list_for_user(costs_list: list):
|
|
25
25
|
:rtype: user_costs_list_to_print: list, total_user_cost: float
|
26
26
|
"""
|
27
27
|
user_costs_list_to_print = []
|
28
|
-
oneoff_aggregated = {}
|
29
28
|
total_user_cost = 0
|
30
29
|
|
31
30
|
for cost in costs_list:
|
32
|
-
if
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
"
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
31
|
+
if "resource_cost" in cost:
|
32
|
+
if cost.get("type", "") == "oneoff":
|
33
|
+
for resource, value in cost["resource_cost"].items():
|
34
|
+
user_costs_list_to_print.append(
|
35
|
+
[
|
36
|
+
cost.get("type", ""),
|
37
|
+
cost.get("namespace", ""),
|
38
|
+
f"{resource}: {float(value):.2f} pln",
|
39
|
+
"-",
|
40
|
+
"-",
|
41
|
+
"<<-",
|
42
|
+
]
|
43
|
+
)
|
44
|
+
total_user_cost += float(cost.get("cost_total", 0))
|
45
|
+
else:
|
46
|
+
resource_cost_str = ", ".join(
|
47
|
+
f"{k}: {float(v):.2f}" for k, v in cost["resource_cost"].items()
|
48
|
+
)
|
49
|
+
start, end = "-", "-"
|
50
|
+
if "datetime_range" in cost and len(cost["datetime_range"]) == 2:
|
51
|
+
start, end = cost["datetime_range"]
|
52
|
+
user_costs_list_to_print.append(
|
53
|
+
[
|
54
|
+
cost.get("type", ""),
|
55
|
+
cost.get("namespace", ""),
|
56
|
+
resource_cost_str,
|
57
|
+
start,
|
58
|
+
end,
|
59
|
+
f"{float(cost.get('cost_total', 0)):.2f} pln",
|
60
|
+
]
|
61
|
+
)
|
62
|
+
total_user_cost += float(cost.get("cost_total", 0))
|
63
|
+
|
64
|
+
if costs_list:
|
65
|
+
user_costs_list_to_print.sort(key=lambda d: f"{d[0]} {d[1]}")
|
66
|
+
return user_costs_list_to_print, total_user_cost
|
67
|
+
|
68
|
+
|
69
|
+
def _get_costs_list_for_user_with_details(costs_list: list):
|
70
|
+
"""Format data in costs list to be displayed in a table and calculate user cost with details
|
71
|
+
|
72
|
+
:param costs_list: list of costs for user
|
73
|
+
:type costs_list: list
|
74
|
+
:return: formatted list of costs and total cost for user
|
75
|
+
:rtype: user_costs_list_to_print: list, total_user_cost: float
|
76
|
+
"""
|
77
|
+
user_costs_list_to_print = []
|
78
|
+
total_user_cost = 0
|
79
|
+
|
80
|
+
for cost in costs_list:
|
81
|
+
# Support both old and new structure
|
82
|
+
record = cost.get("record", {})
|
83
|
+
name = record.get("name", cost.get("name", ""))
|
84
|
+
# id_ = record.get("id", cost.get("id", ""))
|
85
|
+
user_id = record.get("user_id", cost.get("user_id", ""))
|
86
|
+
namespace = record.get("namespace", cost.get("namespace", ""))
|
87
|
+
type_ = record.get("type", cost.get("type", ""))
|
88
|
+
resource_cost = cost.get("resource_cost", {})
|
89
|
+
if type_ == "oneoff":
|
90
|
+
for resource, value in resource_cost.items():
|
91
|
+
user_costs_list_to_print.append(
|
92
|
+
[
|
93
|
+
name,
|
94
|
+
user_id,
|
95
|
+
namespace,
|
96
|
+
type_,
|
97
|
+
f"{resource}: {float(value):.2f} pln",
|
98
|
+
"-",
|
99
|
+
"-",
|
100
|
+
"<<-",
|
101
|
+
]
|
102
|
+
)
|
103
|
+
total_user_cost += float(cost.get("cost_total", 0))
|
53
104
|
else:
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
"
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
105
|
+
resource_cost_str = ", ".join(
|
106
|
+
f"{k}: {float(v):.2f}" for k, v in resource_cost.items()
|
107
|
+
)
|
108
|
+
start = cost.get("calculation_start_time", "-")
|
109
|
+
end = cost.get("calculation_end_time", "-")
|
110
|
+
cost_total = cost.get("cost_total", 0)
|
111
|
+
|
112
|
+
user_costs_list_to_print.append(
|
113
|
+
[
|
114
|
+
name,
|
115
|
+
# id_,
|
116
|
+
user_id,
|
117
|
+
namespace,
|
118
|
+
type_,
|
119
|
+
resource_cost_str,
|
120
|
+
start,
|
121
|
+
end,
|
122
|
+
f"{float(cost_total):.2f} pln",
|
123
|
+
]
|
124
|
+
)
|
125
|
+
total_user_cost += float(cost_total)
|
66
126
|
|
67
127
|
if costs_list:
|
68
|
-
user_costs_list_to_print.sort(key=lambda d: f"{d[0]} {d[
|
128
|
+
user_costs_list_to_print.sort(key=lambda d: f"{d[0]} {d[1]}")
|
69
129
|
return user_costs_list_to_print, total_user_cost
|
70
130
|
|
71
131
|
|
72
|
-
def get_billing_status_message(
|
132
|
+
def get_billing_status_message(billing_records: list, details: list = []):
|
73
133
|
"""Prints billing status for all users in a pretty table
|
74
134
|
|
75
135
|
:param user_list: list of users with costs
|
76
136
|
:type user_list: list
|
77
137
|
"""
|
78
138
|
message = ""
|
79
|
-
|
80
|
-
user_id
|
81
|
-
|
82
|
-
|
139
|
+
users = set(
|
140
|
+
record.get("user_id", "") for record in billing_records
|
141
|
+
) # Get unique user IDs
|
142
|
+
if not users:
|
143
|
+
return "No billing records found."
|
144
|
+
for user in users:
|
145
|
+
user_records = [
|
146
|
+
record for record in billing_records if record["user_id"] == user
|
147
|
+
]
|
148
|
+
costs_list_to_print, _user_cost = _get_costs_list_for_user(user_records)
|
83
149
|
list_headers = _get_status_list_headers()
|
84
|
-
message += f"Billing status for user: {
|
150
|
+
message += f"Billing status for user: {user}\n"
|
85
151
|
message += tabulate(costs_list_to_print, headers=list_headers)
|
86
152
|
message += f"\n\nSummary user cost: {float(_user_cost):.2f} pln\n\n"
|
153
|
+
if details:
|
154
|
+
message += "Detailed billing records:\n"
|
155
|
+
costs_list_to_print, _ = _get_costs_list_for_user_with_details(details)
|
156
|
+
if not costs_list_to_print:
|
157
|
+
message += "No detailed billing records found.\n"
|
158
|
+
else:
|
159
|
+
list_headers = _get_billing_details_list_headers()
|
160
|
+
message += tabulate(costs_list_to_print, headers=list_headers)
|
161
|
+
message += "\n\n"
|
87
162
|
return message
|
88
163
|
|
89
|
-
# "status": "Success",
|
90
|
-
# "reason": "BILLING_INVOICE",
|
91
|
-
# "details": {
|
92
|
-
# "namespace": "pytest",
|
93
|
-
# "cost_total": 0,
|
94
|
-
# "invoice": [
|
95
|
-
# {
|
96
|
-
# "user_id": "e57c8668-6bc3-47c7-85de-903bfc3772b7",
|
97
|
-
# "month": 11,
|
98
|
-
# "year": 2022,
|
99
|
-
# "cost_total": 0,
|
100
|
-
# "details": [],
|
101
|
-
# }
|
102
|
-
# ],
|
103
|
-
# "date_requested": {"year": 2022, "month": 11},
|
104
|
-
|
105
164
|
|
106
165
|
def _get_status_list_headers():
|
107
166
|
"""Generates headers for billing status command
|
@@ -109,7 +168,26 @@ def _get_status_list_headers():
|
|
109
168
|
:return: list of headers
|
110
169
|
:rtype: list
|
111
170
|
"""
|
112
|
-
return ["
|
171
|
+
return ["type", "namespace", "resource breakdown", "start", "end", "cost"]
|
172
|
+
|
173
|
+
|
174
|
+
def _get_billing_details_list_headers():
|
175
|
+
"""Generates headers for billing details command
|
176
|
+
|
177
|
+
:return: list of headers
|
178
|
+
:rtype: list
|
179
|
+
"""
|
180
|
+
return [
|
181
|
+
"name",
|
182
|
+
# "id",
|
183
|
+
"user_id",
|
184
|
+
"namespace",
|
185
|
+
"type",
|
186
|
+
"resource breakdown",
|
187
|
+
"start",
|
188
|
+
"end",
|
189
|
+
"cost_total",
|
190
|
+
]
|
113
191
|
|
114
192
|
|
115
193
|
# TODO: refactor to use: tabulate_a_response(data: list) -> str:
|
@@ -41,7 +41,7 @@ def volume_storage_class_details_response(data: dict) -> str:
|
|
41
41
|
:rtype: str
|
42
42
|
"""
|
43
43
|
# ["details"]["storage_class"] -> storage class name
|
44
|
-
# ["details"]["storage_class_info"] ->
|
44
|
+
# ["details"]["storage_class_info"] -> storage_type, reclaim_policy, volume_binding_mode
|
45
45
|
|
46
46
|
storage_class_headers = [
|
47
47
|
"name",
|
cgc/utils/custom_exceptions.py
CHANGED
@@ -3,33 +3,33 @@
|
|
3
3
|
# every warning has its message that will be returned to print
|
4
4
|
CUSTOM_EXCEPTIONS = {
|
5
5
|
500: {
|
6
|
-
"UNDEFINED": "
|
7
|
-
"USER_KEY_CREATE_ERROR": "Error while creating key",
|
8
|
-
"RESOURCE_SCALE_TEMPLATE_ERROR": "Error while scaling resource - resource not fully compatible CGC",
|
6
|
+
"UNDEFINED": "Undefined exception.",
|
7
|
+
"USER_KEY_CREATE_ERROR": "Error while creating key.",
|
8
|
+
"RESOURCE_SCALE_TEMPLATE_ERROR": "Error while scaling resource - resource not fully compatible with CGC.",
|
9
9
|
},
|
10
10
|
413: {
|
11
|
-
"PVC_CREATE_STORAGE_LIMIT_EXCEEDED": "This request exceeds your storage limits",
|
12
|
-
"PVC_CREATE_NOT_ENOUGH_STORAGE_IN_CLUSTER": "No more storage available",
|
13
|
-
"REQUEST_RESOURCE_LIMIT_EXCEEDED": "This request exceeds your
|
14
|
-
"RESOURCES_NOT_AVAILABLE_IN_CLUSTER": "Requested resources not available",
|
15
|
-
"RESOURCE_SCALE_RESOURCES_LIMIT_EXCEEDED": "This request exceeds your
|
16
|
-
"PVC_CREATE_STORAGE_LIMIT_EXCEEDED": "Storage limit exceeded",
|
11
|
+
"PVC_CREATE_STORAGE_LIMIT_EXCEEDED": "This request exceeds your storage limits.",
|
12
|
+
"PVC_CREATE_NOT_ENOUGH_STORAGE_IN_CLUSTER": "No more storage available.",
|
13
|
+
"REQUEST_RESOURCE_LIMIT_EXCEEDED": "This request exceeds your resource limits.",
|
14
|
+
"RESOURCES_NOT_AVAILABLE_IN_CLUSTER": "Requested resources are not available.",
|
15
|
+
"RESOURCE_SCALE_RESOURCES_LIMIT_EXCEEDED": "This request exceeds your resource limits.",
|
16
|
+
"PVC_CREATE_STORAGE_LIMIT_EXCEEDED": "Storage limit exceeded.",
|
17
17
|
},
|
18
18
|
409: {
|
19
|
-
"PVC_NAME_ALREADY_EXISTS": "
|
20
|
-
"PVC_DELETE_EXCEPTION": "Can't delete mounted volume
|
21
|
-
"RESOURCE_PORTS_ALREADY_EXISTS": "
|
22
|
-
"RESOURCE_TEMPLATE_NAME_ALREADY_EXISTS": "
|
23
|
-
"JOB_CREATE_ALREADY_EXISTS": "
|
24
|
-
"USER_KEY_ALREADY_EXISTS": "
|
25
|
-
"AlreadyExists": "
|
19
|
+
"PVC_NAME_ALREADY_EXISTS": "A volume with this name already exists.",
|
20
|
+
"PVC_DELETE_EXCEPTION": "Can't delete mounted volume. Try with force.",
|
21
|
+
"RESOURCE_PORTS_ALREADY_EXISTS": "A port with this name already exists.",
|
22
|
+
"RESOURCE_TEMPLATE_NAME_ALREADY_EXISTS": "A resource with this name already exists.",
|
23
|
+
"JOB_CREATE_ALREADY_EXISTS": "A job with this name already exists.",
|
24
|
+
"USER_KEY_ALREADY_EXISTS": "A key with this data already exists.",
|
25
|
+
"AlreadyExists": "An object with this name already exists.",
|
26
26
|
},
|
27
27
|
404: {
|
28
|
-
"PVC_CREATE_NO_SC": "Selected disk type and access mode unavailable",
|
28
|
+
"PVC_CREATE_NO_SC": "Selected disk type and access mode are unavailable.",
|
29
29
|
"BILLING_STATUS_NO_DATA": "No data to print.",
|
30
30
|
"NOT_DELETED_ANYTHING_IN_COMPUTE_DELETE": "No app with this name to delete.",
|
31
|
-
"API_KEY_DELETE_ERROR": "No
|
32
|
-
"PVC_MOUNT_NOT_FOUND_TEMPLATE": "
|
31
|
+
"API_KEY_DELETE_ERROR": "No API key with this ID to delete.",
|
32
|
+
"PVC_MOUNT_NOT_FOUND_TEMPLATE": "App resource with this name not found.",
|
33
33
|
"PVC_UNMOUNT_NOT_MOUNTED": "Volume with this name is not mounted.",
|
34
34
|
"PVC_NOT_FOUND": "Volume with this name not found.",
|
35
35
|
"PVC_DELETE_NOT_FOUND": "App with this name not found.",
|
@@ -39,21 +39,21 @@ CUSTOM_EXCEPTIONS = {
|
|
39
39
|
"COMPUTE_RESOURCE_QUOTA_NOT_FOUND": "You do not have enforced limits on your namespace.",
|
40
40
|
"JOB_NOT_FOUND": "Job with this name not found.",
|
41
41
|
"RESOURCE_NOT_FOUND": "Resource with this name not found.",
|
42
|
-
"USER_KEY_NOT_FOUND": "Key with this
|
42
|
+
"USER_KEY_NOT_FOUND": "Key with this ID not found.",
|
43
43
|
"RESOURCE_SCALE_TEMPLATE_NOT_FOUND": "No app with this name.",
|
44
|
-
"NotFound": "Resource not found",
|
44
|
+
"NotFound": "Resource not found.",
|
45
45
|
"USER_SECRET_UPDATE_NOT_FOUND": "Secret not found.",
|
46
46
|
"USER_SECRET_DELETE_NOT_FOUND": "Secret not found.",
|
47
47
|
"PVC_SC_NOT_FOUND": "Storage class with this name is not defined.",
|
48
|
-
"RESOURCE_RESTART_TEMPLATE_NOT_FOUND": "Resource with this name not found",
|
49
|
-
"NOT_DELETED_ANYTHING_IN_RESOURCE_DELETE": "There is nothing to delete with this name",
|
48
|
+
"RESOURCE_RESTART_TEMPLATE_NOT_FOUND": "Resource with this name not found.",
|
49
|
+
"NOT_DELETED_ANYTHING_IN_RESOURCE_DELETE": "There is nothing to delete with this name.",
|
50
50
|
},
|
51
51
|
400: {
|
52
52
|
"WRONG_DATE_FORMAT": "Wrong date format.",
|
53
53
|
"ENTITY_NOT_ALLOWED": "You can't create this entity.",
|
54
54
|
"PVC_MOUNT_ALREADY_MOUNTED": "This volume is already mounted.",
|
55
|
-
"TEMPLATE_NAME_SYSTEM_RESERVED": "You can't create app with this name.",
|
56
|
-
"JOB_LACKS_REQUIRED_PARAMETER": "Job requires container image parameter.",
|
57
|
-
"RESOURCE_PORTS_EXCEPTION": "Resource with this name not found",
|
55
|
+
"TEMPLATE_NAME_SYSTEM_RESERVED": "You can't create an app with this name.",
|
56
|
+
"JOB_LACKS_REQUIRED_PARAMETER": "Job requires a container image parameter.",
|
57
|
+
"RESOURCE_PORTS_EXCEPTION": "Resource with this name not found.",
|
58
58
|
},
|
59
59
|
}
|
cgc/utils/message_utils.py
CHANGED
@@ -63,5 +63,9 @@ def key_error_decorator_for_helpers(func):
|
|
63
63
|
return prepare_error_message(UNKNOWN_ERROR)
|
64
64
|
except (ResponseException, click.ClickException) as err:
|
65
65
|
return prepare_warning_message(err)
|
66
|
+
except KeyboardInterrupt:
|
67
|
+
increment_metric(metric="client.interrupted", is_error=True)
|
68
|
+
# silently exit
|
69
|
+
exit(0)
|
66
70
|
|
67
71
|
return wrapper
|
cgc/utils/prepare_headers.py
CHANGED
@@ -20,7 +20,7 @@ def get_api_url_and_prepare_headers():
|
|
20
20
|
}
|
21
21
|
return get_headers_data.load_user_api_url(), headers
|
22
22
|
|
23
|
-
|
23
|
+
@key_error_decorator_for_helpers
|
24
24
|
def get_url_and_prepare_headers_register(
|
25
25
|
user_id: str, access_key: str, url: str = None, secret: str = None
|
26
26
|
):
|
@@ -39,7 +39,7 @@ def get_url_and_prepare_headers_register(
|
|
39
39
|
}
|
40
40
|
return url, headers
|
41
41
|
|
42
|
-
|
42
|
+
@key_error_decorator_for_helpers
|
43
43
|
def get_url_and_headers_jwt_token():
|
44
44
|
url = f"{get_headers_data.load_user_api_url()}/v1/api/user/create/token"
|
45
45
|
headers = {
|
@@ -63,7 +63,7 @@ def prepare_headers_api_key(user_id: str = None, password: str = None):
|
|
63
63
|
}
|
64
64
|
return headers
|
65
65
|
|
66
|
-
|
66
|
+
@key_error_decorator_for_helpers
|
67
67
|
def get_api_url_and_prepare_headers_version_control():
|
68
68
|
"""Prepares headers for version control request.
|
69
69
|
|
cgc/utils/requests_helper.py
CHANGED
@@ -59,6 +59,10 @@ def call_api(request: EndpointTypes, **kwargs):
|
|
59
59
|
except OSError:
|
60
60
|
increment_metric(metric="client.certificate", is_error=True)
|
61
61
|
click.echo(prepare_error_message(CERTIFICATE_ERROR))
|
62
|
+
except KeyboardInterrupt:
|
63
|
+
increment_metric(metric="client.interrupted", is_error=True)
|
64
|
+
# silently exit
|
65
|
+
sys.exit()
|
62
66
|
except:
|
63
67
|
increment_metric(metric="client.unhandled", is_error=True)
|
64
68
|
click.echo(prepare_error_message(UNKNOWN_ERROR))
|
@@ -1,5 +1,5 @@
|
|
1
|
-
cgc/.env,sha256=
|
2
|
-
cgc/CHANGELOG.md,sha256=
|
1
|
+
cgc/.env,sha256=HL_aZZeVlSqO2b4XjXFvcFow-X89HtNK4yfZn3bFQeg,209
|
2
|
+
cgc/CHANGELOG.md,sha256=DtPZAzR5ZNARX9TRnasZVbY7Tk9ddn913KQw3_smdlo,12119
|
3
3
|
cgc/__init__.py,sha256=d03Xv8Pw4ktNyUHfmicP6XfxYPXnVYLaCZPyUlg_RNQ,326
|
4
4
|
cgc/cgc.py,sha256=3I_Ef0ggX9caaJKJkhfGYSe8XwkHzSWxwGAClMHDnUs,1663
|
5
5
|
cgc/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -20,9 +20,9 @@ cgc/commands/compute/compute_models.py,sha256=5do3kSrzoZSDX1ElSZMmmW3AI9A1oeOGtr
|
|
20
20
|
cgc/commands/compute/compute_responses.py,sha256=R_BcEPxh57JEK_Er83qN22RpuYCDNctlKY6m-5aPx-c,7025
|
21
21
|
cgc/commands/compute/compute_utils.py,sha256=VneJYv8OUiDLYH7u-Gea1vBIx4Tayv3_SRzqfI_i1Sg,10095
|
22
22
|
cgc/commands/compute/billing/__init__.py,sha256=ccjz-AzBCROjuR11qZRM4_62slI9ErmLi27xPUoRPHM,752
|
23
|
-
cgc/commands/compute/billing/billing_cmd.py,sha256=
|
24
|
-
cgc/commands/compute/billing/billing_responses.py,sha256=
|
25
|
-
cgc/commands/compute/billing/billing_utils.py,sha256=
|
23
|
+
cgc/commands/compute/billing/billing_cmd.py,sha256=d6TzvpbLEetEZCLOz3epk29leIJSofRCeCQ0hCW8jvM,3096
|
24
|
+
cgc/commands/compute/billing/billing_responses.py,sha256=R_FQXcU2YhPP80Q0euBsZUc4iVUurJxXY6hLLwazfKY,2514
|
25
|
+
cgc/commands/compute/billing/billing_utils.py,sha256=VQTgZd6pSjwROHb_BpwaATo8LowPyUPdrdbdLbI802Y,8313
|
26
26
|
cgc/commands/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
cgc/commands/db/db_cmd.py,sha256=iXwJzWu4xiaUIfyKSixpnK7-GtoplvfbUN8HHly1YAY,3981
|
28
28
|
cgc/commands/db/db_models.py,sha256=zpMcrDBanLx7YQJ_-PWrQCbh3B-C0qWn1Pt5SnDwsh0,1351
|
@@ -48,7 +48,7 @@ cgc/commands/volume/__init__.py,sha256=Ou3kyb72aaXkrVCfQCVdniA65R2xHsRFgebooG1gf
|
|
48
48
|
cgc/commands/volume/data_model.py,sha256=lLHuKRMe-5mgHL3i48QJn8_S3tJHFMCwu8cJAxXe-PU,1267
|
49
49
|
cgc/commands/volume/volume_cmd.py,sha256=Eylo_V8Tex8OF5IQcAlpo6ggC4FXYfnQunGtat6WwSs,8279
|
50
50
|
cgc/commands/volume/volume_models.py,sha256=eKHYLcAUezoJ1X2ENE-GE2CgE8lynlfT3Hs2PI8zAnY,779
|
51
|
-
cgc/commands/volume/volume_responses.py,sha256=
|
51
|
+
cgc/commands/volume/volume_responses.py,sha256=tuFws-4yMWj3fcjxwclc7FaJbvGK5n3WzK1SwPNGx7Q,4013
|
52
52
|
cgc/commands/volume/volume_utils.py,sha256=6IuDCNT-KAvUUF_EDg5cL9JewTGsbBsZlYd_zKHowCU,1973
|
53
53
|
cgc/sdk/__init__.py,sha256=m8uAD2e_ADbHC4_kaOpLrUk_bHy7wC56rPjhcttclCs,177
|
54
54
|
cgc/sdk/exceptions.py,sha256=99XIzDO6LYKjex715troH-MkGUN7hi2Bit4KHfSHDis,214
|
@@ -71,11 +71,11 @@ cgc/tests/desired_responses/test_volume_list.txt,sha256=vYB1p50BBHD801q7LUdDc_ac
|
|
71
71
|
cgc/utils/__init__.py,sha256=JOvbqyGdFtswXE1TntOqM7XuIg5-t042WzAzvmX7Xtk,3661
|
72
72
|
cgc/utils/click_group.py,sha256=57jEtLJV6944p6gIfuCWmAd_kXfY3rneRngoglYZ9u8,1055
|
73
73
|
cgc/utils/config_utils.py,sha256=rbiA2ZNP2SslJ7zA-my7SB0ZLILBMxGjnzP1JhhGc2g,7525
|
74
|
-
cgc/utils/custom_exceptions.py,sha256=
|
74
|
+
cgc/utils/custom_exceptions.py,sha256=lNpt6Oacd4RqxOQicExUHprLd_a_VtfWS0qVGS3TYtI,3587
|
75
75
|
cgc/utils/get_headers_data.py,sha256=JdEg5vrAHcWfsSJ7poYk3sNIY10OxX7YGVcmua-37lY,413
|
76
|
-
cgc/utils/message_utils.py,sha256=
|
77
|
-
cgc/utils/prepare_headers.py,sha256=
|
78
|
-
cgc/utils/requests_helper.py,sha256=
|
76
|
+
cgc/utils/message_utils.py,sha256=ebU-omCBTEx3Vvq4kF3vG3x0MPv2fotnvxAFX4mR03c,2116
|
77
|
+
cgc/utils/prepare_headers.py,sha256=crjwBvyejtgEQGaL9kJxU4v8Hk6tLn9zaLbMzNIGR6s,2752
|
78
|
+
cgc/utils/requests_helper.py,sha256=L0h_u5PkgSJvNY4Q8eraRebhdLghtDtSeBveCWFzNFQ,2188
|
79
79
|
cgc/utils/response_utils.py,sha256=LCuUFBmkBHmLX5sQ9-WlPShe_yLPXzs8tDS4RUNYJTk,7734
|
80
80
|
cgc/utils/update.py,sha256=AsQwhcBqsjgNPKn6AN6ojt0Ew5otvJXyshys6bjr7DQ,413
|
81
81
|
cgc/utils/version_control.py,sha256=Ssd01sYYp79Y8Bl7_N4BDZLvfrmJ23YoWIBRs4VxHCM,4160
|
@@ -86,9 +86,9 @@ cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
86
86
|
cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
|
87
87
|
cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
|
88
88
|
cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
|
89
|
-
cgcsdk-1.
|
90
|
-
cgcsdk-1.
|
91
|
-
cgcsdk-1.
|
92
|
-
cgcsdk-1.
|
93
|
-
cgcsdk-1.
|
94
|
-
cgcsdk-1.
|
89
|
+
cgcsdk-1.3.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
|
+
cgcsdk-1.3.0.dist-info/METADATA,sha256=ShijHWs9C9oy1NabyeD32sZ8B61dyLrCDANHfIkRYXk,3171
|
91
|
+
cgcsdk-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
92
|
+
cgcsdk-1.3.0.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
|
93
|
+
cgcsdk-1.3.0.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
|
94
|
+
cgcsdk-1.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|