cgcsdk 1.0.3__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 = 3
9
+ MINOR_VERSION = 5
cgc/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
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
+
28
+ ## 1.0.4
29
+
30
+ Release on December 12, 2023
31
+
32
+ * fixed typos
33
+ * command group "cgc compute port" updated logic
34
+ * added "--shm size" option to "cgc compute create" command
35
+ * add support for: weaviate (db, no gpu), t2v-transformers (compute, gpu enabled)
36
+ * weaviate can work with t2v-transformers
37
+ * requires added -d flag: weaviate_enable_modules=text2vec-transformers
38
+ * requires added -d flag: weaviate_transformers_inference_api=<http://name-of-t2v-app:8080>
39
+
3
40
  ## 1.0.3
4
41
 
5
42
  Release on November 21, 2023
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
- from cgc.commands.compute.compute_models import EntityList, GPUsList
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
  )
@@ -74,15 +77,15 @@ def compute_filebrowser_delete():
74
77
 
75
78
 
76
79
  @port_group.command("add", cls=CustomCommand)
77
- @click.argument("name", type=click.STRING)
80
+ @click.argument("app_name", type=click.STRING)
78
81
  @click.option("-p", "--port", "port", type=click.INT, required=True, help="Port number")
79
82
  @click.option(
80
83
  "-n",
81
- "--app-name",
82
- "app_name",
84
+ "--name",
85
+ "port_name",
83
86
  type=click.STRING,
84
87
  required=True,
85
- help="Name of application",
88
+ help="Name of port",
86
89
  )
87
90
  @click.option(
88
91
  "-ni",
@@ -94,13 +97,23 @@ def compute_filebrowser_delete():
94
97
  default=True,
95
98
  help="If set, port will NOT be exposed to the internet. By default port is exposed to the internet.",
96
99
  )
97
- def compute_port_add(name: str, port: int, app_name: str, ingress: bool):
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"
102
115
  __payload = port_modification_payload(
103
- port_name=name,
116
+ port_name=port_name,
104
117
  port_number=port,
105
118
  ingress=ingress,
106
119
  app_name=app_name,
@@ -113,12 +126,12 @@ def compute_port_add(name: str, port: int, app_name: str, ingress: bool):
113
126
  retrieve_and_validate_response_send_metric(__res, metric)
114
127
  )
115
128
  table = fill_missing_values_in_a_response(__res_list)
116
- click.echo(f"Port {name} added successfully to {app_name}. Printing ports:")
129
+ click.echo(f"Port {port_name} added successfully to {app_name}. Printing ports:")
117
130
  click.echo(tabulate_a_response(table))
118
131
 
119
132
 
120
133
  @port_group.command("update", cls=CustomCommand)
121
- @click.argument("name", type=click.STRING)
134
+ @click.argument("app_name", type=click.STRING)
122
135
  @click.option(
123
136
  "-p",
124
137
  "--port",
@@ -130,11 +143,11 @@ def compute_port_add(name: str, port: int, app_name: str, ingress: bool):
130
143
  )
131
144
  @click.option(
132
145
  "-n",
133
- "--app-name",
134
- "app_name",
146
+ "--name",
147
+ "port_name",
135
148
  type=click.STRING,
136
149
  required=True,
137
- help="Name of application",
150
+ help="Name of port",
138
151
  )
139
152
  @click.option(
140
153
  "-ni",
@@ -146,13 +159,23 @@ def compute_port_add(name: str, port: int, app_name: str, ingress: bool):
146
159
  default=True,
147
160
  help="If set, port will NOT be exposed to the internet. By default port is exposed to the internet.",
148
161
  )
149
- def compute_port_update(name: str, port: int, app_name: str, ingress: bool):
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"
154
177
  __payload = port_modification_payload(
155
- port_name=name,
178
+ port_name=port_name,
156
179
  port_number=port,
157
180
  ingress=ingress,
158
181
  app_name=app_name,
@@ -163,26 +186,36 @@ def compute_port_update(name: str, port: int, app_name: str, ingress: bool):
163
186
 
164
187
  retrieve_and_validate_response_send_metric(__res, metric)
165
188
 
166
- click.echo(f"Port {name} updated successfully in {app_name}")
189
+ click.echo(f"Port {port_name} updated successfully in {app_name}")
167
190
 
168
191
 
169
192
  @port_group.command("delete", cls=CustomCommand)
170
- @click.argument("name", type=click.STRING)
193
+ @click.argument("app_name", type=click.STRING)
171
194
  @click.option(
172
195
  "-n",
173
- "--app-name",
174
- "app_name",
196
+ "--name",
197
+ "port_name",
175
198
  type=click.STRING,
176
199
  required=True,
177
- help="Name of application",
200
+ help="Name of port",
178
201
  )
179
- def compute_port_delete(name: str, app_name: str):
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"
184
217
  __payload = port_delete_payload(
185
- port_name=name,
218
+ port_name=port_name,
186
219
  app_name=app_name,
187
220
  )
188
221
  __res = call_api(
@@ -191,7 +224,7 @@ def compute_port_delete(name: str, app_name: str):
191
224
 
192
225
  retrieve_and_validate_response_send_metric(__res, metric)
193
226
 
194
- click.echo(f"Port {name} deleted successfully from {app_name}")
227
+ click.echo(f"Port {port_name} deleted successfully from {app_name}")
195
228
 
196
229
 
197
230
  @port_group.command("list", cls=CustomCommand)
@@ -217,8 +250,11 @@ def compute_port_list(app_name: str):
217
250
 
218
251
 
219
252
  @compute_group.command("create", cls=CustomCommand)
220
- @click.argument("entity", type=click.Choice(EntityList.get_list()))
221
- @click.option("-n", "--name", "name", type=click.STRING, required=True)
253
+ @click.argument("entity", type=click.Choice(ComputesList.get_list()))
254
+ @click.argument("startup_command", type=click.File("r"), default="-", required=False)
255
+ @click.option(
256
+ "-n", "--name", "name", type=click.STRING, required=True, help="Desired app name"
257
+ )
222
258
  @click.option(
223
259
  "-g",
224
260
  "--gpu",
@@ -258,6 +294,13 @@ def compute_port_list(app_name: str):
258
294
  multiple=True,
259
295
  help="List of volume names to be mounted with default mount path",
260
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
+ )
261
304
  @click.option(
262
305
  "-d",
263
306
  "--resource-data",
@@ -265,6 +308,40 @@ def compute_port_list(app_name: str):
265
308
  multiple=True,
266
309
  help="List of optional arguments to be passed to the app, key=value format",
267
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
+ )
330
+ @click.option(
331
+ "--shm",
332
+ "shm_size",
333
+ type=click.IntRange(0, 1024, clamp=True),
334
+ default=0,
335
+ help="Size of shared memory in Gi",
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
+ )
268
345
  def compute_create(
269
346
  entity: str,
270
347
  gpu: int,
@@ -272,8 +349,15 @@ def compute_create(
272
349
  cpu: int,
273
350
  memory: int,
274
351
  volumes: list[str],
352
+ volume_full_path: str,
275
353
  resource_data: list[str],
354
+ config_maps_data: list[str],
276
355
  name: str,
356
+ shm_size: int,
357
+ image_name: str,
358
+ startup_command: str,
359
+ repository_secret: str,
360
+ node_port_enabled: bool,
277
361
  ):
278
362
  """
279
363
  Create an app in user namespace.
@@ -288,13 +372,29 @@ def compute_create(
288
372
  :type memory: int
289
373
  :param volumes: list of volumes to mount
290
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
291
377
  :param resource_data: list of optional arguments to be passed to the app
292
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]
293
381
  :param name: name of app
294
382
  :type name: str
383
+ :param shm_size: size of shared memory
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
295
393
  """
296
394
  api_url, headers = get_api_url_and_prepare_headers()
297
395
  url = f"{api_url}/v1/api/resource/create"
396
+ if startup_command != "-":
397
+ startup_command = process_stdin(startup_command)
298
398
  metric = "compute.create"
299
399
  __payload = compute_create_payload(
300
400
  name=name,
@@ -303,8 +403,15 @@ def compute_create(
303
403
  memory=memory,
304
404
  gpu=gpu,
305
405
  volumes=volumes,
406
+ volume_full_path=volume_full_path,
306
407
  resource_data=resource_data,
408
+ config_maps_data=config_maps_data,
307
409
  gpu_type=gpu_type,
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,
308
415
  )
309
416
  # Extra keys allowed for payload:
310
417
  # 1.
@@ -346,15 +453,15 @@ def compute_create(
346
453
 
347
454
 
348
455
  @compute_group.command("delete", cls=CustomCommand)
349
- @click.argument("name", type=click.STRING)
350
- def compute_delete_cmd(name: str):
456
+ @click.argument("app_name", type=click.STRING)
457
+ def compute_delete_cmd(app_name: str):
351
458
  """
352
459
  Delete an app from user namespace.
353
460
  \f
354
- :param name: name of app to delete
355
- :type name: str
461
+ :param app_name: name of app to delete
462
+ :type app_name: str
356
463
  """
357
- resource_delete(name)
464
+ resource_delete(app_name)
358
465
 
359
466
 
360
467
  compute_group.add_command(filebrowser_group)
@@ -382,3 +489,23 @@ def resource_list(detailed: bool):
382
489
  )
383
490
 
384
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)
@@ -9,7 +9,7 @@ class CGCEntityList(Enum):
9
9
  return [el.value for el in cls]
10
10
 
11
11
 
12
- class EntityList(CGCEntityList):
12
+ class ComputesList(CGCEntityList):
13
13
  """List of templates in cgc-server
14
14
 
15
15
  :param Enum: name of template
@@ -24,7 +24,9 @@ class EntityList(CGCEntityList):
24
24
  LABEL_STUDIO = "label-studio"
25
25
  COMFY_UI = "comfy-ui"
26
26
  RAG = "rag"
27
- DEEPSTREAM = "deapstream"
27
+ DEEPSTREAM = "deepstream"
28
+ T2V_TRANSFORMERS = "t2v-transformers"
29
+ CUSTOM = "custom"
28
30
 
29
31
 
30
32
  class DatabasesList(CGCEntityList):
@@ -37,6 +39,7 @@ class DatabasesList(CGCEntityList):
37
39
  MONGODB = "mongodb"
38
40
  POSTGRESQL = "postgresql"
39
41
  REDIS = "redis"
42
+ WEAVIATE = "weaviate"
40
43
  # MINIO = "minio"
41
44
 
42
45
 
@@ -47,6 +50,7 @@ class GPUsList(CGCEntityList):
47
50
  :type Enum: str
48
51
  """
49
52
 
53
+ V100 = "V100"
50
54
  A100 = "A100"
51
- # V100 = "V100"
52
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