cgcsdk 1.0.4__py3-none-any.whl → 1.0.5__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 CHANGED
@@ -6,4 +6,4 @@ CONFIG_FILE_NAME = cfg.json
6
6
  TMP_DIR = .tmp
7
7
  RELEASE = 1
8
8
  MAJOR_VERSION = 0
9
- MINOR_VERSION = 4
9
+ MINOR_VERSION = 5
cgc/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.0.5
4
+
5
+ Release on March 7, 2024
6
+
7
+ * added new compute template: custom
8
+ * custom template can be any docker image
9
+ * custom template can use docker registry, that requires authentication
10
+ * during compute create, new flag available: -fp, --full-path
11
+ * allows to mount volume with full path; works with one volume only
12
+ * added support for adding for compute create config_map_data
13
+ * added for compute create
14
+ * --startup-command flag to set startup command (overrides default command in Dockerfile)
15
+ * --repository-secret flag to set docker registry secret from namespace
16
+ * --image flag to set docker image for custom template
17
+ * managing compute ports, require user confirmation
18
+ * resource commands
19
+ * add compute custom create and resource delete to cgc sdk
20
+ * add compute port add, delete, update, list to cgc sdk
21
+ * add compute and db **list** to cgc sdk
22
+ * add resource ready command to cgc sdk
23
+ * on-premises support for node_port_enabled
24
+ * added new command: cgc context replace-env "path_to_env_file"
25
+ * added new command: cgc context get-env "where_to_save_env_file"
26
+ * added new command: cgc compute list-mounts
27
+
3
28
  ## 1.0.4
4
29
 
5
30
  Release on December 12, 2023
@@ -10,7 +35,7 @@ Release on December 12, 2023
10
35
  * add support for: weaviate (db, no gpu), t2v-transformers (compute, gpu enabled)
11
36
  * weaviate can work with t2v-transformers
12
37
  * requires added -d flag: weaviate_enable_modules=text2vec-transformers
13
- * requires added -d flag: weaviate_transformers_inference_api=http://name-of-t2v-app:8080
38
+ * requires added -d flag: weaviate_transformers_inference_api=<http://name-of-t2v-app:8080>
14
39
 
15
40
  ## 1.0.3
16
41
 
cgc/commands/cgc_cmd.py CHANGED
@@ -13,6 +13,7 @@ from cgc.commands.auth.auth_cmd import auth_register
13
13
  from cgc.utils import set_environment_data, check_if_config_exist, list_all_config_files
14
14
  from cgc.commands.cgc_helpers import table_of_user_context_files
15
15
  from cgc.utils.config_utils import config_path
16
+ from cgc.utils.consts.env_consts import ENV_FILE_PATH
16
17
 
17
18
 
18
19
  @click.command("rm", cls=CustomCommand)
@@ -115,3 +116,9 @@ def folder_of_contexts():
115
116
  click.echo(
116
117
  "You can check location of that folder on different machine with the same command."
117
118
  )
119
+
120
+
121
+ @context_group.command("get-env-path", cls=CustomCommand)
122
+ def get_env_path():
123
+ """Displays current environment file path"""
124
+ click.echo(f"Current environment file path: {ENV_FILE_PATH}")
@@ -1,33 +1,45 @@
1
- from typing import List
2
- from cgc.utils import quick_sort
3
- from cgc.utils.config_utils import read_from_cfg
4
- from tabulate import tabulate
5
-
6
-
7
- def table_of_user_context_files(config_files: List[str]):
8
- # print tabulate of: [context NR | namespace | user_id]
9
- headers = ["Context No.", "Namespace", "User ID"]
10
- contexts = []
11
- contexts_nrs = []
12
- for file in config_files:
13
- file_context = []
14
- file_context.append(
15
- int(file.split(".")[0]) if file != "cfg.json" else 1
16
- ) # should never throw exception with good config_file list
17
- contexts_nrs.append(file_context[0])
18
- file_data = read_from_cfg(None, file)
19
- values_to_read = ["namespace", "user_id"]
20
- for k in values_to_read:
21
- try:
22
- value = file_data[k]
23
- except KeyError:
24
- value = None
25
- file_context.append(value)
26
- contexts.append(file_context)
27
-
28
- contexts_nrs_sorted = quick_sort(contexts_nrs)
29
- contexts_sorted = []
30
- for context in contexts_nrs_sorted:
31
- contexts_sorted.append(contexts[contexts_nrs.index(context)])
32
-
33
- return tabulate(contexts_sorted, headers=headers)
1
+ from typing import List
2
+ from cgc.utils import quick_sort
3
+ from cgc.utils.config_utils import read_from_cfg
4
+ from tabulate import tabulate
5
+
6
+
7
+ def table_of_user_context_files(config_files: List[str]):
8
+ # print tabulate of: [context NR | namespace | user_id]
9
+ headers = ["Context No.", "Namespace", "User ID"]
10
+ contexts = []
11
+ contexts_nrs = []
12
+ for file in config_files:
13
+ file_context = []
14
+ file_context.append(
15
+ int(file.split(".")[0]) if file != "cfg.json" else 1
16
+ ) # should never throw exception with good config_file list
17
+ contexts_nrs.append(file_context[0])
18
+ file_data = read_from_cfg(None, file)
19
+ values_to_read = ["namespace", "user_id"]
20
+ for k in values_to_read:
21
+ try:
22
+ value = file_data[k]
23
+ except KeyError:
24
+ value = None
25
+ file_context.append(value)
26
+ contexts.append(file_context)
27
+
28
+ contexts_nrs_sorted = quick_sort(contexts_nrs)
29
+ contexts_sorted = []
30
+ for context in contexts_nrs_sorted:
31
+ contexts_sorted.append(contexts[contexts_nrs.index(context)])
32
+
33
+ return tabulate(contexts_sorted, headers=headers)
34
+
35
+ def process_stdin(input_file: str):
36
+ full_stdin = []
37
+ try:
38
+ for line in input_file:
39
+ line = line.strip()
40
+ if line and line != '|':
41
+ full_stdin.append(line.rstrip('\n')) # Remove newline characters
42
+ except TypeError:
43
+ return None
44
+ return " ".join(full_stdin)
45
+
@@ -1,10 +1,13 @@
1
1
  import json
2
+ import sys
2
3
  import click
4
+ from cgc.commands.cgc_helpers import process_stdin
3
5
 
4
6
  from cgc.commands.compute.compute_models import ComputesList, GPUsList
5
7
  from cgc.commands.compute.compute_responses import (
6
8
  compute_create_filebrowser_response,
7
9
  compute_create_response,
10
+ compute_list_mounts_response,
8
11
  compute_list_response,
9
12
  get_compute_port_list,
10
13
  )
@@ -96,6 +99,16 @@ def compute_filebrowser_delete():
96
99
  )
97
100
  def compute_port_add(app_name: str, port_name: str, port: int, ingress: bool):
98
101
  """Add a port to a running resource"""
102
+ while True:
103
+ click.echo(
104
+ 'Adding a port to a running resource will expose it to the internet. If you want to add a port without exposing it to the internet, use the "--no-ingress" flag.'
105
+ )
106
+ click.echo("Resource will be restarted after port addition.")
107
+ answer = input("Do you want to continue? (Y/N): ").lower()
108
+ if answer in ("y", "yes"):
109
+ break
110
+ if answer in ("n", "no"):
111
+ sys.exit()
99
112
  api_url, headers = get_api_url_and_prepare_headers()
100
113
  url = f"{api_url}/v1/api/resource/ports?port_modification_mode=ADD"
101
114
  metric = "resource.ports.add"
@@ -148,6 +161,16 @@ def compute_port_add(app_name: str, port_name: str, port: int, ingress: bool):
148
161
  )
149
162
  def compute_port_update(app_name: str, port_name: str, port: int, ingress: bool):
150
163
  """Update a port in a running resource"""
164
+ while True:
165
+ click.echo(
166
+ 'Updating a port in a running resource will expose it to the internet. If you want to update a port without exposing it to the internet, use the "--no-ingress" flag.'
167
+ )
168
+ click.echo("Resource will be restarted after port update.")
169
+ answer = input("Do you want to continue? (Y/N): ").lower()
170
+ if answer in ("y", "yes"):
171
+ break
172
+ if answer in ("n", "no"):
173
+ sys.exit()
151
174
  api_url, headers = get_api_url_and_prepare_headers()
152
175
  url = f"{api_url}/v1/api/resource/ports?port_modification_mode=UPDATE"
153
176
  metric = "resource.ports.update"
@@ -178,6 +201,16 @@ def compute_port_update(app_name: str, port_name: str, port: int, ingress: bool)
178
201
  )
179
202
  def compute_port_delete(app_name: str, port_name: str):
180
203
  """Delete a port from a running resource"""
204
+ while True:
205
+ click.echo(
206
+ 'Deleting a port from a running resource will expose it to the internet. If you want to delete a port without exposing it to the internet, use the "--no-ingress" flag.'
207
+ )
208
+ click.echo("Resource will be restarted after port delete.")
209
+ answer = input("Do you want to continue? (Y/N): ").lower()
210
+ if answer in ("y", "yes"):
211
+ break
212
+ if answer in ("n", "no"):
213
+ sys.exit()
181
214
  api_url, headers = get_api_url_and_prepare_headers()
182
215
  url = f"{api_url}/v1/api/resource/ports?port_modification_mode=DELETE"
183
216
  metric = "resource.ports.delete"
@@ -218,6 +251,7 @@ def compute_port_list(app_name: str):
218
251
 
219
252
  @compute_group.command("create", cls=CustomCommand)
220
253
  @click.argument("entity", type=click.Choice(ComputesList.get_list()))
254
+ @click.argument("startup_command", type=click.File("r"), default="-", required=False)
221
255
  @click.option(
222
256
  "-n", "--name", "name", type=click.STRING, required=True, help="Desired app name"
223
257
  )
@@ -260,6 +294,13 @@ def compute_port_list(app_name: str):
260
294
  multiple=True,
261
295
  help="List of volume names to be mounted with default mount path",
262
296
  )
297
+ @click.option(
298
+ "-fp",
299
+ "--full-path",
300
+ "volume_full_path",
301
+ type=click.STRING,
302
+ help="If set, full path will be used for volume mount. Valid for 1 volume.",
303
+ )
263
304
  @click.option(
264
305
  "-d",
265
306
  "--resource-data",
@@ -267,6 +308,25 @@ def compute_port_list(app_name: str):
267
308
  multiple=True,
268
309
  help="List of optional arguments to be passed to the app, key=value format",
269
310
  )
311
+ @click.option(
312
+ "--image",
313
+ "image_name",
314
+ type=click.STRING,
315
+ help="Image to be used by the app",
316
+ )
317
+ @click.option(
318
+ "--repository-secret",
319
+ "repository_secret",
320
+ type=click.STRING,
321
+ help="Use secret to pull image from private repository",
322
+ )
323
+ @click.option(
324
+ "-cm",
325
+ "--config-map",
326
+ "config_maps_data",
327
+ multiple=True,
328
+ help="List of optional arguments to be passed to the app, key=value format",
329
+ )
270
330
  @click.option(
271
331
  "--shm",
272
332
  "shm_size",
@@ -274,6 +334,14 @@ def compute_port_list(app_name: str):
274
334
  default=0,
275
335
  help="Size of shared memory in Gi",
276
336
  )
337
+ @click.option(
338
+ "--node-port-enabled",
339
+ "node_port_enabled",
340
+ type=click.BOOL,
341
+ default=False,
342
+ is_flag=True,
343
+ help="If set, app will be exposed to the internet",
344
+ )
277
345
  def compute_create(
278
346
  entity: str,
279
347
  gpu: int,
@@ -281,9 +349,15 @@ def compute_create(
281
349
  cpu: int,
282
350
  memory: int,
283
351
  volumes: list[str],
352
+ volume_full_path: str,
284
353
  resource_data: list[str],
354
+ config_maps_data: list[str],
285
355
  name: str,
286
356
  shm_size: int,
357
+ image_name: str,
358
+ startup_command: str,
359
+ repository_secret: str,
360
+ node_port_enabled: bool,
287
361
  ):
288
362
  """
289
363
  Create an app in user namespace.
@@ -298,15 +372,29 @@ def compute_create(
298
372
  :type memory: int
299
373
  :param volumes: list of volumes to mount
300
374
  :type volumes: list[str]
375
+ :param volume_full_path: if set, full path will be used for volume mount
376
+ :type volume_full_path: str
301
377
  :param resource_data: list of optional arguments to be passed to the app
302
378
  :type resource_data: list[str]
379
+ :param config_maps_data: list of optional arguments to be passed to the app
380
+ :type config_maps_data: list[str]
303
381
  :param name: name of app
304
382
  :type name: str
305
383
  :param shm_size: size of shared memory
306
384
  :type shm_size: int
385
+ :param image_name: name of image to be used by the app
386
+ :type image_name: str
387
+ :param startup_command: command to be executed on app startup; it is stdin input
388
+ :type startup_command: str
389
+ :param repository_secret: use secret to pull image from private repository
390
+ :type repository_secret: str
391
+ :param node_port_enabled: if set, app ports will be exposed to the network via node addresses
392
+ :type node_port_enabled: bool
307
393
  """
308
394
  api_url, headers = get_api_url_and_prepare_headers()
309
395
  url = f"{api_url}/v1/api/resource/create"
396
+ if startup_command != "-":
397
+ startup_command = process_stdin(startup_command)
310
398
  metric = "compute.create"
311
399
  __payload = compute_create_payload(
312
400
  name=name,
@@ -315,9 +403,15 @@ def compute_create(
315
403
  memory=memory,
316
404
  gpu=gpu,
317
405
  volumes=volumes,
406
+ volume_full_path=volume_full_path,
318
407
  resource_data=resource_data,
408
+ config_maps_data=config_maps_data,
319
409
  gpu_type=gpu_type,
320
410
  shm_size=shm_size,
411
+ image_name=image_name,
412
+ startup_command=startup_command,
413
+ repository_secret=repository_secret,
414
+ node_port_enabled=node_port_enabled,
321
415
  )
322
416
  # Extra keys allowed for payload:
323
417
  # 1.
@@ -395,3 +489,23 @@ def resource_list(detailed: bool):
395
489
  )
396
490
 
397
491
  click.echo(table)
492
+
493
+
494
+ @compute_group.command("list-mounts", cls=CustomCommand)
495
+ def compute_list_mounts():
496
+ """
497
+ List all apps for user namespace.
498
+ """
499
+ api_url, headers = get_api_url_and_prepare_headers()
500
+ url = f"{api_url}/v1/api/resource/list?resource_type=compute"
501
+ metric = "compute.list"
502
+ __res = call_api(
503
+ request=EndpointTypes.get,
504
+ url=url,
505
+ headers=headers,
506
+ )
507
+ table = compute_list_mounts_response(
508
+ retrieve_and_validate_response_send_metric(__res, metric),
509
+ )
510
+
511
+ click.echo(table)
@@ -26,6 +26,7 @@ class ComputesList(CGCEntityList):
26
26
  RAG = "rag"
27
27
  DEEPSTREAM = "deepstream"
28
28
  T2V_TRANSFORMERS = "t2v-transformers"
29
+ CUSTOM = "custom"
29
30
 
30
31
 
31
32
  class DatabasesList(CGCEntityList):
@@ -49,6 +50,7 @@ class GPUsList(CGCEntityList):
49
50
  :type Enum: str
50
51
  """
51
52
 
53
+ V100 = "V100"
52
54
  A100 = "A100"
53
- # V100 = "V100"
54
55
  A5000 = "A5000"
56
+ H100 = "H100"
@@ -1,6 +1,6 @@
1
1
  from cgc.telemetry.basic import change_gauge
2
2
  from cgc.telemetry.basic import setup_gauge
3
- from cgc.commands.compute.compute_utills import get_app_list
3
+ from cgc.commands.compute.compute_utills import get_app_list, get_app_mounts
4
4
  from cgc.utils.config_utils import get_namespace
5
5
  from cgc.utils.message_utils import key_error_decorator_for_helpers
6
6
  from cgc.commands.compute import NoAppsToList
@@ -55,6 +55,22 @@ def compute_list_response(detailed: bool, data: dict) -> str:
55
55
  return tabulate_a_response(table)
56
56
 
57
57
 
58
+ @key_error_decorator_for_helpers
59
+ def compute_list_mounts_response(data: dict) -> str:
60
+ """Create response string for compute list_mounts command.
61
+
62
+ :param response: dict object from API response.
63
+ :type response: requests.Response
64
+ :return: Response string.
65
+ :rtype: str
66
+ """
67
+ pod_list = data["details"]["pods_list"]
68
+ list_of_json_data = get_app_mounts(pod_list)
69
+ table = fill_missing_values_in_a_response(list_of_json_data)
70
+
71
+ return tabulate_a_response(table)
72
+
73
+
58
74
  @key_error_decorator_for_helpers
59
75
  def compute_create_filebrowser_response(data: dict) -> str:
60
76
  """Create response string for compute create_filebrowser command
@@ -1,3 +1,25 @@
1
+ from ast import main
2
+ import cgc.utils.consts.env_consts as env_consts
3
+
4
+ def list_get_mounted_volumes_paths(volume_list: list) -> str:
5
+ """Formats and returns list of PVC volumes mounted to an app.
6
+
7
+ :param volume_list: list of all volumes mounted to an app
8
+ :type volume_list: list
9
+ :return: list of volume paths
10
+ :rtype: str
11
+ """
12
+ volume_name_list = []
13
+ for volume in volume_list:
14
+ volume_type = volume.get("type")
15
+ if volume_type == "PVC":
16
+ volume_mount_path = volume.get("mount_path")
17
+ volume_name_list.append(volume_mount_path)
18
+ volumes_mounted = (
19
+ ", ".join(volume_name_list) if len(volume_name_list) != 0 else None
20
+ )
21
+ return volumes_mounted
22
+
1
23
  def list_get_mounted_volumes(volume_list: list) -> str:
2
24
  """Formats and returns list of PVC volumes mounted to an app.
3
25
 
@@ -17,6 +39,29 @@ def list_get_mounted_volumes(volume_list: list) -> str:
17
39
  )
18
40
  return volumes_mounted
19
41
 
42
+ def get_app_mounts(pod_list:list) -> list:
43
+ output_data = []
44
+
45
+ for pod in pod_list:
46
+ try:
47
+ main_container_name = pod["labels"]["entity"]
48
+ try:
49
+ main_container = [x for x in pod["containers"] if x["name"] == main_container_name][0]
50
+ except IndexError:
51
+ raise Exception("Parser was unable to find main container in server output in container list")
52
+ volumes_mounted = list_get_mounted_volumes(main_container["mounts"])
53
+ volumes_paths = list_get_mounted_volumes_paths(main_container["mounts"])
54
+ pod_data = {
55
+ "name": pod["labels"]["app-name"],
56
+ "type": pod["labels"]["entity"],
57
+ "status": pod["status"],
58
+ "volumes_mounted": volumes_mounted,
59
+ "volumes_paths": volumes_paths,
60
+ }
61
+ output_data.append(pod_data)
62
+ except KeyError:
63
+ pass
64
+ return output_data
20
65
 
21
66
  def get_app_list(pod_list: list, detailed: bool) -> list:
22
67
  """Formats and returns list of apps to print.
@@ -117,10 +162,16 @@ def compute_create_payload(
117
162
  cpu,
118
163
  memory,
119
164
  volumes: list,
165
+ volume_full_path: str,
120
166
  resource_data: list = [],
167
+ config_maps_data: list = [],
121
168
  gpu: int = 0,
122
169
  gpu_type: str = None,
123
170
  shm_size: int = 0,
171
+ image_name: str = "",
172
+ startup_command: str = "",
173
+ repository_secret: str = "",
174
+ node_port_enabled: bool = False,
124
175
  ):
125
176
  """
126
177
  Create payload for app creation.
@@ -137,17 +188,31 @@ def compute_create_payload(
137
188
  "gpu": gpu,
138
189
  "memory": memory,
139
190
  "gpu_type": gpu_type,
191
+ "full_mount_path": volume_full_path,
140
192
  **shm_payload,
141
193
  }
142
194
  }
143
195
  try:
144
196
  if len(volumes) != 0:
145
- payload["resource_data"]["pv_volume"] = volumes
197
+ if not volume_full_path:
198
+ payload["resource_data"]["pv_volume"] = volumes
199
+ elif volume_full_path and len(volumes) != 1:
200
+ raise Exception(
201
+ "Volume full path can only be used with a single volume"
202
+ )
203
+ else:
204
+ payload["resource_data"]["pv_volume"] = volumes
146
205
  except TypeError:
147
206
  pass
148
207
  try:
208
+ resource_data_dict = {"resource_data": {}}
209
+ if node_port_enabled:
210
+ if not env_consts.ON_PREMISES:
211
+ raise Exception(
212
+ "NodePort is supported in on-premises environments only."
213
+ )
214
+ resource_data_dict["resource_data"]["node_port_enabled"] = True
149
215
  if len(resource_data) != 0:
150
- resource_data_dict = {"resource_data": {}}
151
216
  for resource in resource_data:
152
217
  try:
153
218
  key, value = resource.split("=")
@@ -156,9 +221,36 @@ def compute_create_payload(
156
221
  raise Exception(
157
222
  "Invalid resource data format. Use key=value format"
158
223
  )
224
+ if image_name:
225
+ resource_data_dict["resource_data"]["custom_image"] = image_name
226
+ if startup_command:
227
+ resource_data_dict["resource_data"]["custom_command"] = startup_command
228
+ if repository_secret:
229
+ resource_data_dict["resource_data"][
230
+ "image_pull_secret_name"
231
+ ] = repository_secret
232
+ if resource_data_dict["resource_data"] != {}:
159
233
  payload["template_specific_data"] = resource_data_dict
160
234
  except TypeError:
161
235
  pass
236
+ try:
237
+ if len(config_maps_data) != 0:
238
+ config_maps_data_dict = {}
239
+ for config_map in config_maps_data:
240
+ try:
241
+ key, value = config_map.split(
242
+ "="
243
+ ) # where key is name of config map and value is data
244
+ config_maps_data_dict[key] = (
245
+ value # value is dict, ex.: {"key": "value"}
246
+ )
247
+ except ValueError:
248
+ raise Exception(
249
+ "Invalid config map data format. Use key=value format"
250
+ )
251
+ payload["config_maps_data"] = config_maps_data_dict
252
+ except TypeError:
253
+ pass
162
254
  return payload
163
255
 
164
256
 
@@ -22,7 +22,7 @@ from cgc.utils.click_group import CustomGroup, CustomCommand
22
22
  from cgc.utils.requests_helper import call_api, EndpointTypes
23
23
 
24
24
 
25
- @click.group(name="resource", cls=CustomGroup, hidden=True)
25
+ @click.group(name="resource", cls=CustomGroup, hidden=False)
26
26
  def resource_group():
27
27
  """
28
28
  Management of templates.
cgc/sdk/__init__.py CHANGED
@@ -1,3 +1,6 @@
1
1
  from cgc.sdk.mongodb import get_mongo_access as mongo_client
2
2
  from cgc.sdk.redis import get_redis_access as redis_client
3
3
  from cgc.sdk.postgresql import get_postgresql_access as postgresql_client
4
+
5
+ import cgc.sdk.resource as resource
6
+ import cgc.sdk.exceptions as exceptions
cgc/sdk/exceptions.py ADDED
@@ -0,0 +1,7 @@
1
+ from cgc.commands.exceptions import ResponseException
2
+
3
+
4
+ class SDKException(ResponseException):
5
+ def __init__(self, code: int, *args, **kwargs):
6
+ self.code = code
7
+ super().__init__(*args, **kwargs)
cgc/sdk/resource.py ADDED
@@ -0,0 +1,425 @@
1
+ import json as _json
2
+ import re
3
+ import cgc.sdk.exceptions as _exceptions
4
+ import cgc.utils.prepare_headers as _prepare_headers
5
+ import cgc.commands.compute.compute_utills as _compute_utills
6
+ import cgc.utils.requests_helper as _requests_helper
7
+ import cgc.utils.response_utils as _response_utils
8
+ from enum import Enum as _Enum
9
+ from cgc.commands.compute.compute_models import GPUsList
10
+
11
+
12
+ def start_function_loop(function, infinite: bool = True, *args, **kwargs):
13
+ """
14
+ Starts a function in a loop until it gets response code 200.
15
+
16
+ Args:
17
+ function: The function to be executed.
18
+ infinite: A boolean indicating whether the loop should be infinite (default: True).
19
+ *args: Positional arguments to be passed to the function.
20
+ **kwargs: Keyword arguments to be passed to the function.
21
+
22
+ Raises:
23
+ _SDKException: If the app fails to start within 10 iterations (when infinite is False).
24
+
25
+ Returns:
26
+ None
27
+ """
28
+ from time import sleep
29
+
30
+ counter = 0
31
+ try:
32
+ response = function(*args, **kwargs)
33
+ if type(response) is bool:
34
+ while not response:
35
+ counter += 1
36
+ if not infinite and counter > 10:
37
+ raise _exceptions.SDKException(-1, response)
38
+ sleep(5)
39
+ response = function(*args, **kwargs)
40
+ elif type(response) is dict:
41
+ while response["code"] != 200:
42
+ counter += 1
43
+ if not infinite and counter > 10:
44
+ raise _exceptions.SDKException(-1, response)
45
+ sleep(5)
46
+ response = function(*args, **kwargs)
47
+ else:
48
+ raise _exceptions.SDKException(-1, response)
49
+ except _exceptions.SDKException as e:
50
+ import logging
51
+
52
+ if e.code == 409:
53
+ logging.warning(e)
54
+ else:
55
+ raise e
56
+ else:
57
+ return response
58
+
59
+
60
+ def stop_function_loop(function, infinite: bool = True, *args, **kwargs):
61
+ """
62
+ Stop function loop, proceed on response code 200.
63
+
64
+ Args:
65
+ function: The function to be stopped.
66
+ infinite (bool): Flag indicating whether the loop should run infinitely or not.
67
+ *args: Variable length argument list to be passed to the function.
68
+ **kwargs: Arbitrary keyword arguments to be passed to the function.
69
+
70
+ Raises:
71
+ _SDKException: If the app fails to stop within 10 attempts (when infinite is False).
72
+
73
+ Returns:
74
+ None
75
+ """
76
+ from time import sleep
77
+
78
+ counter = 0
79
+ response = function(*args, **kwargs)
80
+ if type(response) is bool:
81
+ while not response:
82
+ counter += 1
83
+ if not infinite and counter > 10:
84
+ raise _exceptions.SDKException(-1, response)
85
+ sleep(5)
86
+ response = function(*args, **kwargs)
87
+ elif type(response) is dict:
88
+ while response["code"] != 200:
89
+ counter += 1
90
+ if not infinite and counter > 10:
91
+ raise _exceptions.SDKException(-1, response)
92
+ sleep(5)
93
+ response = function(*args, **kwargs)
94
+ else:
95
+ raise _exceptions.SDKException(-1, response)
96
+ return response
97
+
98
+
99
+ def compute_list():
100
+ """
101
+ List all compute apps for user namespace.
102
+ """
103
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
104
+ url = f"{api_url}/v1/api/resource/list?resource_type=compute"
105
+ metric = "compute.list"
106
+ __res = _requests_helper.call_api(
107
+ request=_requests_helper.EndpointTypes.get,
108
+ url=url,
109
+ headers=headers,
110
+ )
111
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
112
+ __res, metric
113
+ )
114
+
115
+
116
+ class ResourceTypes(_Enum):
117
+ compute = "compute"
118
+ db = "db"
119
+
120
+
121
+ def _resource_status_ready(name: str, response: dict):
122
+ """
123
+ Check if a resource is ready.
124
+
125
+ Args:
126
+ name: The name of the resource.
127
+ response: The response from the API call.
128
+
129
+ Returns:
130
+ The response from the API call.
131
+ """
132
+ for pod in response["details"]["pods_list"]:
133
+ try:
134
+ if pod["labels"]["app-name"] == name:
135
+ return pod["status"] == "Running"
136
+ except KeyError:
137
+ return False
138
+ return False
139
+
140
+
141
+ def resource_ready(
142
+ name: str, resource_type: ResourceTypes = ResourceTypes.compute
143
+ ) -> bool:
144
+ """
145
+ Check if a resource is ready.
146
+
147
+ Args:
148
+ name: The name of the resource.
149
+ resource_type: The type of resource to check (default: ResourceTypes.compute).
150
+
151
+ Returns:
152
+ bool: A boolean indicating whether the resource is ready.
153
+ """
154
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
155
+ url = f"{api_url}/v1/api/resource/list?resource_type={resource_type.value}"
156
+ metric = "resource.list"
157
+ __res = _requests_helper.call_api(
158
+ request=_requests_helper.EndpointTypes.get,
159
+ url=url,
160
+ headers=headers,
161
+ )
162
+ validated_response = (
163
+ _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
164
+ __res, metric
165
+ )
166
+ )
167
+ return _resource_status_ready(name, validated_response)
168
+
169
+
170
+ def db_list():
171
+ """
172
+ List all db apps for user namespace.
173
+ """
174
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
175
+ url = f"{api_url}/v1/api/resource/list?resource_type=db"
176
+ metric = "compute.list"
177
+ __res = _requests_helper.call_api(
178
+ request=_requests_helper.EndpointTypes.get,
179
+ url=url,
180
+ headers=headers,
181
+ )
182
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
183
+ __res, metric
184
+ )
185
+
186
+
187
+ def compute_create_custom(
188
+ name: str,
189
+ image_name: str,
190
+ cpu: int = 1,
191
+ memory: int = 2,
192
+ shm_size: int = 0,
193
+ gpu: int = 0,
194
+ gpu_type: str = "A5000",
195
+ volumes: list = [],
196
+ volume_full_path: str = "",
197
+ startup_command: str = "",
198
+ repository_secret: str = "",
199
+ resource_data: list = [],
200
+ config_maps_data: list = [],
201
+ node_port_enabled: bool = False,
202
+ ):
203
+ """
204
+ Create a custom compute resource.
205
+
206
+ :param name: The name of the compute resource.
207
+ :type name: str
208
+ :param image_name: The name of the image to use for the compute resource.
209
+ :type image_name: str,
210
+ :param cpu: The number of CPUs for the compute resource, defaults to 1.
211
+ :type cpu: int, optional
212
+ :param memory: The amount of memory (in GB) for the compute resource, defaults to 2.
213
+ :type memory: int, optional
214
+ :param shm_size: The size of the shared memory (in GB) for the compute resource, defaults to 0.
215
+ :type shm_size: int, optional
216
+ :param gpu: The number of GPUs for the compute resource, defaults to 0.
217
+ :type gpu: int, optional
218
+ :param gpu_type: The type of GPU for the compute resource, defaults to "A5000".
219
+ :type gpu_type: str, optional
220
+ :param volumes: The list of volumes to attach to the compute resource, defaults to [].
221
+ :type volumes: list, optional
222
+ :param volume_full_path: The full path of the volume, defaults to "".
223
+ :type volume_full_path: str, optional
224
+ :param startup_command: The startup command for the compute resource, defaults to "".
225
+ :type startup_command: str, optional
226
+ :param repository_secret: The secret for accessing the repository, defaults to "".
227
+ :type repository_secret: str, optional
228
+ :param resource_data: The additional resource data, defaults to [].
229
+ :type resource_data: list, optional
230
+ :param config_maps_data: The additional config maps data, defaults to [].
231
+ :type config_maps_data: list, optional
232
+ :param node_port_enabled: A flag indicating whether the node port is enabled, defaults to False.
233
+ :type node_port_enabled: bool, optional
234
+ :raises _SDKException: If the image name is not provided.
235
+ :raises _SDKException: If an invalid GPU type is specified.
236
+ :return: The response from the API call.
237
+ :rtype: _type_
238
+ """
239
+ if not image_name:
240
+ raise _exceptions.SDKException(-2, "Image name is required")
241
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
242
+ url = f"{api_url}/v1/api/resource/create"
243
+ metric = "compute.create"
244
+ gpu_type = gpu_type.upper()
245
+ if gpu_type not in GPUsList.get_list():
246
+ raise _exceptions.SDKException(-3, f"Invalid GPU type: {gpu_type}")
247
+ __payload = _compute_utills.compute_create_payload(
248
+ name=name,
249
+ entity="custom",
250
+ cpu=cpu,
251
+ memory=memory,
252
+ gpu=gpu,
253
+ gpu_type=gpu_type,
254
+ volumes=volumes,
255
+ volume_full_path=volume_full_path,
256
+ resource_data=resource_data,
257
+ config_maps_data=config_maps_data,
258
+ shm_size=shm_size,
259
+ image_name=image_name,
260
+ startup_command=startup_command,
261
+ repository_secret=repository_secret,
262
+ node_port_enabled=node_port_enabled,
263
+ )
264
+
265
+ __res = _requests_helper.call_api(
266
+ request=_requests_helper.EndpointTypes.post,
267
+ url=url,
268
+ headers=headers,
269
+ data=_json.dumps(__payload),
270
+ )
271
+
272
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
273
+ __res, metric
274
+ )
275
+
276
+
277
+ def resource_update_port(
278
+ name: str, port_name: str, new_port: int, ingress: bool = True
279
+ ):
280
+ """
281
+ Update a port for an app using backend endpoint.
282
+
283
+ :param name: name of app to edit
284
+ :type name: str
285
+ :param port_name: name of port to edit
286
+ :type port_name: str
287
+ :param new_port: new port number
288
+ :type new_port: int
289
+ :return: response from the API call
290
+ :rtype: dict
291
+ """
292
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
293
+ url = f"{api_url}/v1/api/resource/ports?port_modification_mode=UPDATE"
294
+ metric = "resource.ports.update"
295
+ __payload = _compute_utills.port_modification_payload(
296
+ port_name=port_name,
297
+ port_number=new_port,
298
+ ingress=ingress,
299
+ app_name=name,
300
+ )
301
+ __res = _requests_helper.call_api(
302
+ request=_requests_helper.EndpointTypes.post,
303
+ url=url,
304
+ headers=headers,
305
+ data=_json.dumps(__payload),
306
+ )
307
+
308
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
309
+ __res, metric
310
+ )
311
+
312
+
313
+ def resource_add_port(name: str, port_name: str, new_port: int, ingress: bool = True):
314
+ """
315
+ Add a port for an app using backend endpoint.
316
+
317
+ :param name: name of app to edit
318
+ :type name: str
319
+ :param port_name: name of port to add
320
+ :type port_name: str
321
+ :param new_port: new port number
322
+ :type new_port: int
323
+ :return: response from the API call
324
+ :rtype: dict
325
+ """
326
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
327
+ url = f"{api_url}/v1/api/resource/ports?port_modification_mode=ADD"
328
+ metric = "resource.ports.add"
329
+ __payload = _compute_utills.port_modification_payload(
330
+ port_name=port_name,
331
+ port_number=new_port,
332
+ ingress=ingress,
333
+ app_name=name,
334
+ )
335
+ __res = _requests_helper.call_api(
336
+ request=_requests_helper.EndpointTypes.post,
337
+ url=url,
338
+ headers=headers,
339
+ data=_json.dumps(__payload),
340
+ )
341
+
342
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
343
+ __res, metric
344
+ )
345
+
346
+
347
+ def resource_delete_port(
348
+ name: str,
349
+ port_name: str,
350
+ ):
351
+ """
352
+ Add a port for an app using backend endpoint.
353
+
354
+ :param name: name of app to edit
355
+ :type name: str
356
+ :param port_name: name of port to delete
357
+ :type port_name: str
358
+ :return: response from the API call
359
+ :rtype: dict
360
+ """
361
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
362
+ url = f"{api_url}/v1/api/resource/ports?port_modification_mode=DELETE"
363
+ metric = "resource.ports.delete"
364
+ __payload = _compute_utills.port_delete_payload(
365
+ port_name=port_name,
366
+ app_name=name,
367
+ )
368
+ __res = _requests_helper.call_api(
369
+ request=_requests_helper.EndpointTypes.post,
370
+ url=url,
371
+ headers=headers,
372
+ data=_json.dumps(__payload),
373
+ )
374
+
375
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
376
+ __res, metric
377
+ )
378
+
379
+
380
+ def resource_list_ports(name: str):
381
+ """
382
+ List ports for an app using backend endpoint.
383
+
384
+ :param name: name of app to list ports for
385
+ :type name: str
386
+ :return: response from the API call
387
+ :rtype: dict
388
+ """
389
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
390
+ url = f"{api_url}/v1/api/resource/ports?resource_name={name}"
391
+ metric = "resource.ports.list"
392
+ __res = _requests_helper.call_api(
393
+ request=_requests_helper.EndpointTypes.get,
394
+ url=url,
395
+ headers=headers,
396
+ )
397
+
398
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
399
+ __res, metric
400
+ )
401
+
402
+
403
+ def resource_delete(name: str):
404
+ """
405
+ Delete an app using backend endpoint.
406
+
407
+ :param name: name of app to delete
408
+ :type name: str
409
+ :return: response from the API call
410
+ :rtype: dict
411
+ """
412
+ api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
413
+ url = f"{api_url}/v1/api/resource/delete"
414
+ metric = "resource.delete"
415
+ __payload = _compute_utills.compute_delete_payload(name=name)
416
+ __res = _requests_helper.call_api(
417
+ request=_requests_helper.EndpointTypes.delete,
418
+ url=url,
419
+ headers=headers,
420
+ data=_json.dumps(__payload),
421
+ )
422
+
423
+ return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
424
+ __res, metric
425
+ )
@@ -38,6 +38,7 @@ TMP_DIR = os.getenv("TMP_DIR")
38
38
  RELEASE = int(os.getenv("RELEASE"))
39
39
  MAJOR_VERSION = int(os.getenv("MAJOR_VERSION"))
40
40
  MINOR_VERSION = int(os.getenv("MINOR_VERSION"))
41
+ ON_PREMISES = True if os.getenv("ON_PREMISES") == "1" else False
41
42
 
42
43
 
43
44
  def get_config_file_name():
@@ -14,7 +14,7 @@ CUSTOM_EXCEPTIONS = {
14
14
  409: {
15
15
  "PVC_NAME_ALREADY_EXISTS": "Volume with this name already exists.",
16
16
  "PVC_DELETE_EXCEPTION": "Can't delete mounted volume, try with force",
17
- "COMPUTE_TEMPLATE_NAME_ALREADY_EXISTS": "Template with this name already exists.",
17
+ "RESOURCE_TEMPLATE_NAME_ALREADY_EXISTS": "Template with this name already exists.",
18
18
  },
19
19
  404: {
20
20
  "PVC_CREATE_NO_SC": "Selected disk type and access mode unavailable",
@@ -14,6 +14,7 @@ from cgc.utils.consts.message_consts import (
14
14
  from cgc.utils.custom_exceptions import CUSTOM_EXCEPTIONS
15
15
  from cgc.telemetry.basic import increment_metric
16
16
  from cgc.utils.config_utils import get_namespace
17
+ from cgc.sdk.exceptions import SDKException
17
18
 
18
19
 
19
20
  def _get_response_json_error_message(response_json: dict):
@@ -22,6 +23,65 @@ def _get_response_json_error_message(response_json: dict):
22
23
  return UNKNOWN_ERROR
23
24
 
24
25
 
26
+ def retrieve_and_validate_response_send_metric_for_sdk(
27
+ response: requests.Response, metric: str, json_return: bool = True
28
+ ):
29
+ """Checks if server is available and user is authorized
30
+
31
+ :param response: dict object from API response.
32
+ :type response: requests.Response
33
+ """
34
+ error_message = UNKNOWN_ERROR
35
+ try:
36
+ try:
37
+ if metric is not None:
38
+ metric = f"{get_namespace()}.{metric}"
39
+ except NoNamespaceInConfig:
40
+ metric = f"unknown-namespace.{metric}"
41
+ except NoConfigFileFound:
42
+ print("No config file found. Please use:")
43
+ print("cgc register")
44
+ metric = f"bad-client.{metric}"
45
+
46
+ if response.status_code == 200:
47
+ increment_metric(
48
+ metric=metric, is_error=False
49
+ ) # if metric is None, will not increment metric
50
+ if json_return:
51
+ return response.json()
52
+ else:
53
+ return response
54
+ increment_metric(
55
+ metric=metric, is_error=False
56
+ ) # ALL "VALID" error messages are not Errors
57
+ if response.status_code == 401:
58
+ raise SDKException(response.status_code, UNAUTHORIZED_ERROR)
59
+ elif response.status_code == 302:
60
+ raise SDKException(response.status_code, ENDPOINT_DISABLED)
61
+ else:
62
+ try:
63
+ response_json = response.json()
64
+ if "detail" in response_json:
65
+ raise SDKException(response.status_code, response_json["detail"])
66
+ else:
67
+ raise SDKException(
68
+ response.status_code,
69
+ CUSTOM_EXCEPTIONS[response.status_code][
70
+ response_json["reason"]
71
+ ],
72
+ )
73
+ except KeyError as e:
74
+ error_message = _get_response_json_error_message(response_json)
75
+ increment_metric(metric=metric, is_error=True)
76
+ raise SDKException(response.status_code, error_message) from e
77
+
78
+ except (AttributeError, json.JSONDecodeError) as e:
79
+ increment_metric(metric=metric, is_error=True)
80
+ raise SDKException(
81
+ response.status_code, f"Response reading error. {error_message}"
82
+ ) from e
83
+
84
+
25
85
  def retrieve_and_validate_response_send_metric(
26
86
  response: requests.Response, metric: str, json_return: bool = True
27
87
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cgcsdk
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: Comtegra GPU Cloud REST API client
5
5
  Author: Comtegra AI Team
6
6
  Author-email: ai@comtegra.pl
@@ -1,12 +1,12 @@
1
- cgc/.env,sha256=wLSnB9As4-SThAcqVSi5Ujgqa9D5wkwCtw1YOyxPnBg,209
2
- cgc/CHANGELOG.md,sha256=olpBc8YP2X4ia-oiZUSFK6CgbgqLL6qwRlyqJaRe8Mk,6312
1
+ cgc/.env,sha256=4WIMONz4kDhj4OoFOm52Rauw6Ba84NI3HKiCBB3t1-o,209
2
+ cgc/CHANGELOG.md,sha256=WHHXunoy5D1qKLhG3InzyLPU8gRYtZrLD_jBljBZBLw,7433
3
3
  cgc/__init__.py,sha256=d03Xv8Pw4ktNyUHfmicP6XfxYPXnVYLaCZPyUlg_RNQ,326
4
4
  cgc/cgc.py,sha256=kPLg3h-3kjlMBiwZGOM7yvXJ7pzkVglAbWWQ7KX8jeY,1377
5
5
  cgc/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  cgc/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- cgc/commands/cgc_cmd.py,sha256=Tvjr_dtRur_5G-rhLFgvNNS-T19bI3bRHQ0mDbjmwLw,4061
7
+ cgc/commands/cgc_cmd.py,sha256=tct8ZBplLsqD-gjxsnwx_uJo1361iOnZvvxGIGYMU_c,4310
8
8
  cgc/commands/cgc_cmd_responses.py,sha256=ToPjvGf3zUbFLhLwv-BV8ZAvAHg1xS99iprM928MnOY,2026
9
- cgc/commands/cgc_helpers.py,sha256=i5M0xYwEAYFKeA9Rvp5x3xYBGrxtUPU8y8uegt6VkhY,1207
9
+ cgc/commands/cgc_helpers.py,sha256=nDq9qLRpF2yu9vvYvDjnIYKudTNdrRop0XAOLYm6zxs,1509
10
10
  cgc/commands/exceptions.py,sha256=l3Sms3D2fxSpLQQQEYeLWxO3to82myTQ0VFgFYdQdLU,45
11
11
  cgc/commands/auth/__init__.py,sha256=K8HkHHotMnK7SQRAst5rx_wprHEphPo_w2KToEymjAY,399
12
12
  cgc/commands/auth/auth_cmd.py,sha256=jfijV-C0uOnpj5LcOgsW7Sn5JiemZ-nuQRQUEjEvaEk,3856
@@ -17,27 +17,29 @@ cgc/commands/billing/billing_cmd.py,sha256=T1E5WW5Z-RlzddaotimTuvLvIbGihNpStIBET
17
17
  cgc/commands/billing/billing_responses.py,sha256=HAD5N-Odx3Jz1OmhO4v66rHoXpTYIOGlXDsrs0da9dk,1949
18
18
  cgc/commands/billing/billing_utils.py,sha256=zXLbBBcWeOgur-r0OKiIjaKeaxMNxASXWzCTeTsyC6o,4711
19
19
  cgc/commands/compute/__init__.py,sha256=lCdLfZ0ECSHtXEUSwq5YRHH85yXHchSsz8ZJvmClPtI,239
20
- cgc/commands/compute/compute_cmd.py,sha256=jqD6Jxggh80UT80yV3SqsGL-ye3HW4gsiQCT6PFIYtc,11173
21
- cgc/commands/compute/compute_models.py,sha256=bRm0uYfdVlnXqIopkUQBZxl94mQtGjaW3l62absqWfA,1101
22
- cgc/commands/compute/compute_responses.py,sha256=AUSfmzUPr-4RSeXaQaykk9yFKCh-YtQcxllcp_Mi4as,5298
23
- cgc/commands/compute/compute_utills.py,sha256=GwqKTRtIeVSCRWxCMQhPWDSF-zTb0QUh9ZAW95SYCX8,4945
20
+ cgc/commands/compute/compute_cmd.py,sha256=_4yt6sGWwbqZSUcc-VjtF2luTwyeQVbu2MsDXpUL1lY,15266
21
+ cgc/commands/compute/compute_models.py,sha256=DzktT7nR6eG3po6iIayQjnczmnZruAl00Q9o3okqdng,1139
22
+ cgc/commands/compute/compute_responses.py,sha256=eOmcllyOqPYqN0kSUzSpuC2S1rFmkkawgc_F-0-LSIQ,5807
23
+ cgc/commands/compute/compute_utills.py,sha256=oANUoJRt0L2VDKoMsAhEkE5lfigjRrX9fJjhWpG1Mc4,8615
24
24
  cgc/commands/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  cgc/commands/db/db_cmd.py,sha256=zVcA5GHsW2sZ0Q826q7Va-4Y9iGmw-J7aBPwu-PWF8I,3293
26
26
  cgc/commands/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  cgc/commands/debug/debug_cmd.py,sha256=kuAuh5YOqzGFjoiYZwfM9FJ1z5OeSpC0JAIUEzS83lM,412
28
28
  cgc/commands/resource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- cgc/commands/resource/resource_cmd.py,sha256=TwA8TnCEIni3BmSqkgBKuKHKQ3TMKYO4_F72sErQxPs,4020
29
+ cgc/commands/resource/resource_cmd.py,sha256=nHIlUxtMJQnffy2TfcJy_N1Q6juMPsCK7gc542-fhmU,4021
30
30
  cgc/commands/resource/resource_responses.py,sha256=sES7mAi_Cv5B6Z3I_6eUOqVwOr2HgMO45cz8MiNZetQ,197
31
31
  cgc/commands/volume/__init__.py,sha256=Ou3kyb72aaXkrVCfQCVdniA65R2xHsRFgebooG1gflA,384
32
32
  cgc/commands/volume/data_model.py,sha256=meprXdaXLo3mMTZta1ks1-BJ7G0rO16qi_ycH-sXJpY,1295
33
33
  cgc/commands/volume/volume_cmd.py,sha256=C2K6PclAYhRm5optlmSxj4KujonYu8kdu1kGJQ0Nck4,7609
34
34
  cgc/commands/volume/volume_responses.py,sha256=WefUohXxXZ9Znfc6P2XjoAM2RlA19hMKcVaW-xG9HWs,3199
35
35
  cgc/commands/volume/volume_utils.py,sha256=n6s0FgpsYyxFMp_JdMRCzRi5Ar3_Svg9JDvWSdX4lhk,1919
36
- cgc/sdk/__init__.py,sha256=XhdIQ-K0mFVl3m4UPftBc7ZYDSsrPpLi2g2Scbk9XI0,194
36
+ cgc/sdk/__init__.py,sha256=2KWwBgq1wxwrB1uTYfN4TvKoNAWiy5qugb1L7YPspfo,271
37
+ cgc/sdk/exceptions.py,sha256=99XIzDO6LYKjex715troH-MkGUN7hi2Bit4KHfSHDis,214
37
38
  cgc/sdk/handlers.py,sha256=ECCHNe1pErsXFlmwHewsWRvYqzAZ5j5TrSqwernpLJk,868
38
39
  cgc/sdk/mongodb.py,sha256=TJ2XU7nilNRXLOIpQQPrRiVxHN2TaVM5QOSuMRtNDVs,7221
39
40
  cgc/sdk/postgresql.py,sha256=ziXaMMwjSF3k1OAID3F9npqWVxreQaoZ8wn7X8x1FZw,1637
40
41
  cgc/sdk/redis.py,sha256=W5wS9Sgyv4098yzWAwG7qEk4HEDwscE3JmWgPC3NCzc,2844
42
+ cgc/sdk/resource.py,sha256=X1MqX2dYom5G8zu27onTdZR7tZK6-VlBgH9MXoQwfLY,13460
41
43
  cgc/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
44
  cgc/telemetry/basic.py,sha256=XagCcyH4QSEPmfiQ1WCjqXslnJO6IaJCY0AMPySd5rc,3175
43
45
  cgc/tests/__init__.py,sha256=8aI3MVpkzaj0_UX02kZCtY5vmGO0rnq0mw2H04-OHf8,102743
@@ -53,23 +55,23 @@ cgc/tests/desired_responses/test_volume_list.txt,sha256=vYB1p50BBHD801q7LUdDc_ac
53
55
  cgc/utils/__init__.py,sha256=l9JF-WnvmhlolxbDKlJPsxquZ-fjtvv7wKvn2zpu5IM,3466
54
56
  cgc/utils/click_group.py,sha256=Scfw8eMIyt2dE1ezUq2JuiI-E_LklqXQXJEr7L-EG6A,633
55
57
  cgc/utils/config_utils.py,sha256=VFTrcN9QeOByIobhh48TfAm-BWON4PN8zX0H8PdUNTU,2729
56
- cgc/utils/custom_exceptions.py,sha256=DXhEjei0dW-dHOSCZitiuzBQEhDSBheGBHi6gZ-jO3Q,2067
58
+ cgc/utils/custom_exceptions.py,sha256=V50fXz09c_VNzLkgVNCFihIN1o-lUqRV0XMpy8zTgH8,2068
57
59
  cgc/utils/message_utils.py,sha256=jdUHtR2-gEvyxYu1T0OY-a6cBz5N8TatRaxKNuNcJtU,1766
58
60
  cgc/utils/prepare_headers.py,sha256=FK04VFreStH6xcfgP8s5zmePilQPn0bKhCajgV0f2sY,2546
59
61
  cgc/utils/requests_helper.py,sha256=ghn8LTxWqfRvy7BXQdxD4VHX8b-ypHkbnFXY05ig7_A,2050
60
- cgc/utils/response_utils.py,sha256=d-NvGr-U4iBaYiWmiYhonTwOeYGPMhHWk1Me4ov0tIs,5018
62
+ cgc/utils/response_utils.py,sha256=9vJqAt2UFJ1n-oesFPe6CB_ooGoStjl-twY_31Jt4_I,7374
61
63
  cgc/utils/update.py,sha256=AsQwhcBqsjgNPKn6AN6ojt0Ew5otvJXyshys6bjr7DQ,413
62
64
  cgc/utils/version_control.py,sha256=VsqNzNYWDvU3VsoPuYIaZV7Img-K2_DmEOHd7rZlRxk,3267
63
65
  cgc/utils/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- cgc/utils/consts/env_consts.py,sha256=IhruFRld61AgNKk_E3Ld4a043mZ1GhRSrpFhhRo4sz4,1221
66
+ cgc/utils/consts/env_consts.py,sha256=sHO1vzWZALiXB4Ej2E7S5v0i-DfTwHBIIC1fIP_MocI,1286
65
67
  cgc/utils/consts/message_consts.py,sha256=8CIe3N_HL6Pj-gSArkPkpegsvm-QMWxqqnSgtzG08Qw,1218
66
68
  cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
69
  cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
68
70
  cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
69
71
  cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
70
- cgcsdk-1.0.4.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
- cgcsdk-1.0.4.dist-info/METADATA,sha256=m68OWhjTFY9kmugshwzQ0Y4n2sYydU76K6fvNFK6Tjw,1418
72
- cgcsdk-1.0.4.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
73
- cgcsdk-1.0.4.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
74
- cgcsdk-1.0.4.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
75
- cgcsdk-1.0.4.dist-info/RECORD,,
72
+ cgcsdk-1.0.5.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ cgcsdk-1.0.5.dist-info/METADATA,sha256=rVxdoWqe-3QobxnPQ9LS40TLAAnVEkvUxkXDwM-5f98,1418
74
+ cgcsdk-1.0.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
75
+ cgcsdk-1.0.5.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
76
+ cgcsdk-1.0.5.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
77
+ cgcsdk-1.0.5.dist-info/RECORD,,
File without changes