cgcsdk 1.0.7__py3-none-any.whl → 1.0.10__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.
Files changed (47) hide show
  1. cgc/.env +1 -1
  2. cgc/CHANGELOG.md +29 -0
  3. cgc/cgc.py +4 -0
  4. cgc/commands/auth/__init__.py +2 -2
  5. cgc/commands/auth/auth_cmd.py +14 -2
  6. cgc/commands/auth/auth_responses.py +9 -2
  7. cgc/commands/auth/auth_utils.py +6 -5
  8. cgc/commands/cgc_models.py +9 -0
  9. cgc/commands/compute/compute_cmd.py +1 -1
  10. cgc/commands/compute/compute_models.py +1 -23
  11. cgc/commands/compute/compute_responses.py +1 -1
  12. cgc/commands/db/db_cmd.py +8 -2
  13. cgc/commands/db/db_models.py +37 -0
  14. cgc/commands/exceptions.py +8 -0
  15. cgc/commands/jobs/job_utils.py +37 -32
  16. cgc/commands/jobs/jobs_responses.py +1 -1
  17. cgc/commands/resource/resource_cmd.py +3 -2
  18. cgc/commands/user/__init__.py +14 -0
  19. cgc/commands/user/keys_cmd.py +179 -0
  20. cgc/commands/user/keys_models.py +16 -0
  21. cgc/commands/user/keys_responses.py +44 -0
  22. cgc/commands/user/keys_utils.py +79 -0
  23. cgc/commands/user/secret_cmd.py +154 -0
  24. cgc/commands/user/secret_responses.py +44 -0
  25. cgc/commands/user/secret_utils.py +60 -0
  26. cgc/sdk/__init__.py +0 -2
  27. cgc/sdk/job.py +1 -1
  28. cgc/sdk/resource.py +6 -6
  29. cgc/tests/responses_tests.py +1 -1
  30. cgc/utils/__init__.py +8 -0
  31. cgc/utils/config_utils.py +5 -1
  32. cgc/utils/consts/env_consts.py +1 -1
  33. cgc/utils/custom_exceptions.py +3 -0
  34. cgc/utils/message_utils.py +1 -1
  35. cgc/utils/prepare_headers.py +22 -13
  36. cgc/utils/requests_helper.py +2 -2
  37. cgcsdk-1.0.10.dist-info/LICENSE +0 -0
  38. {cgcsdk-1.0.7.dist-info → cgcsdk-1.0.10.dist-info}/METADATA +3 -7
  39. {cgcsdk-1.0.7.dist-info → cgcsdk-1.0.10.dist-info}/RECORD +44 -36
  40. cgc/sdk/handlers.py +0 -24
  41. cgc/sdk/mongodb.py +0 -204
  42. cgc/sdk/redis.py +0 -91
  43. /cgc/commands/compute/{compute_utills.py → compute_utils.py} +0 -0
  44. /cgcsdk-1.0.7.dist-info/LICENSE → /cgc/commands/user/secret_models.py +0 -0
  45. {cgcsdk-1.0.7.dist-info → cgcsdk-1.0.10.dist-info}/WHEEL +0 -0
  46. {cgcsdk-1.0.7.dist-info → cgcsdk-1.0.10.dist-info}/entry_points.txt +0 -0
  47. {cgcsdk-1.0.7.dist-info → cgcsdk-1.0.10.dist-info}/top_level.txt +0 -0
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 = 7
9
+ MINOR_VERSION = 10
cgc/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.0.10
4
+
5
+ Release on April 24, 2024
6
+
7
+ * added new command group: cgc secrets
8
+ * add - add new secret
9
+ * list - list all secrets in the namespace
10
+ * delete - delete secret (requires to be owner of the secret)
11
+ * update - update secret (requires to be owner of the secret)
12
+
13
+ ## 1.0.9
14
+
15
+ Release on April 23, 2024
16
+
17
+ * updated cgc to work with cgc-server 1.0.10
18
+ * updated registration process
19
+ * updated available apps, so it reflects changes in cgc-server
20
+ * hotfix: listening jobs, which are not Running
21
+
22
+ ## 1.0.8
23
+
24
+ Release on April 11, 2024
25
+
26
+ * added new command group: cgc keys
27
+ * ssh create - create new ssh key, for ssh access
28
+ * ssh list - list all ssh keys
29
+ * ssh delete - delete ssh key
30
+ * ssh update - update ssh key
31
+
3
32
  ## 1.0.7
4
33
 
5
34
  Release on April 04, 2024
cgc/cgc.py CHANGED
@@ -19,6 +19,8 @@ from cgc.commands.cgc_cmd import (
19
19
  resource_events,
20
20
  context_group,
21
21
  )
22
+ from cgc.commands.user.keys_cmd import keys_group
23
+ from cgc.commands.user.secret_cmd import secret_group
22
24
 
23
25
  from cgc.utils.version_control import check_version, _get_version
24
26
  from cgc.utils.click_group import CustomGroup
@@ -46,6 +48,8 @@ cli.add_command(cgc_status)
46
48
  cli.add_command(sending_telemetry_permission)
47
49
  cli.add_command(cgc_logs)
48
50
  cli.add_command(job_group)
51
+ cli.add_command(keys_group)
52
+ cli.add_command(secret_group)
49
53
 
50
54
  if __name__ == "__main__" or __name__ == "cgc.cgc":
51
55
  cli()
@@ -7,9 +7,9 @@ class AuthCommandException(ResponseException):
7
7
 
8
8
  class NoNamespaceInConfig(AuthCommandException):
9
9
  def __init__(self) -> None:
10
- super().__init__(f"Namespace not readable from config file.")
10
+ super().__init__("Namespace not readable from config file.")
11
11
 
12
12
 
13
13
  class NoConfigFileFound(AuthCommandException):
14
14
  def __init__(self) -> None:
15
- super().__init__(f"Config does not exists.")
15
+ super().__init__("Config does not exists.")
@@ -9,7 +9,11 @@ from cgc.commands.auth.auth_utils import (
9
9
  auth_delete_api_key,
10
10
  auth_list_api_keys,
11
11
  )
12
- from cgc.utils.prepare_headers import get_url_and_prepare_headers_register
12
+ from cgc.utils.prepare_headers import (
13
+ get_url_and_prepare_headers_register,
14
+ load_user_api_url,
15
+ load_user_cgc_secret,
16
+ )
13
17
  from cgc.utils.cryptography import rsa_crypto
14
18
  from cgc.utils.click_group import CustomCommand, CustomGroup
15
19
  from cgc.utils.requests_helper import call_api, EndpointTypes
@@ -20,6 +24,7 @@ from cgc.utils.response_utils import (
20
24
  )
21
25
  from cgc.utils import (
22
26
  check_if_config_exist,
27
+ require_answer_loop,
23
28
  require_confirm_loop,
24
29
  find_first_available_config_name,
25
30
  )
@@ -55,9 +60,14 @@ def auth_register(config_filename: str = "cfg.json"):
55
60
  require_confirm_loop("Do you want to add new context?")
56
61
  config_filename = find_first_available_config_name()
57
62
 
63
+ cgc_api_url = require_answer_loop("Enter CGC server address", load_user_api_url())
64
+ cgc_secret = require_answer_loop("Enter CGC secret", load_user_cgc_secret())
65
+
58
66
  user_id = input("User ID: ")
59
67
  access_key = input("Access key: ")
60
- url, headers = get_url_and_prepare_headers_register(user_id, access_key)
68
+ url, headers = get_url_and_prepare_headers_register(
69
+ user_id, access_key, cgc_api_url, cgc_secret
70
+ )
61
71
  metric = "auth.register"
62
72
  pub_key_bytes, priv_key_bytes = rsa_crypto.key_generate_pair()
63
73
  __payload = pub_key_bytes
@@ -74,6 +84,8 @@ def auth_register(config_filename: str = "cfg.json"):
74
84
  user_id,
75
85
  priv_key_bytes,
76
86
  config_filename,
87
+ cgc_api_url,
88
+ cgc_secret,
77
89
  )
78
90
  )
79
91
 
@@ -8,14 +8,21 @@ from cgc.utils.message_utils import key_error_decorator_for_helpers
8
8
 
9
9
 
10
10
  @key_error_decorator_for_helpers
11
- def auth_register_response(response, user_id, priv_key_bytes, config_filename) -> str:
11
+ def auth_register_response(
12
+ response, user_id, priv_key_bytes, config_filename, cgc_api_url, cgc_secret
13
+ ) -> str:
12
14
  TMP_DIR_PATH = os.path.join(get_config_path(), TMP_DIR)
13
15
  unzip_dir, namespace = auth_utils.save_and_unzip_file(response)
14
16
  aes_key, password = auth_utils.get_aes_key_and_password(unzip_dir, priv_key_bytes)
15
17
 
16
18
  os.environ["CONFIG_FILE_NAME"] = config_filename
17
19
  save_to_config(
18
- user_id=user_id, password=password, aes_key=aes_key, namespace=namespace
20
+ user_id=user_id,
21
+ password=password,
22
+ aes_key=aes_key,
23
+ namespace=namespace,
24
+ cgc_api_url=cgc_api_url,
25
+ cgc_secret=cgc_secret,
19
26
  )
20
27
  auth_utils.auth_create_api_key_with_save()
21
28
  shutil.rmtree(TMP_DIR_PATH)
@@ -10,7 +10,7 @@ from cgc.utils.config_utils import save_to_config
10
10
  from cgc.utils.config_utils import read_from_cfg
11
11
  from cgc.utils.cryptography import rsa_crypto
12
12
  from cgc.utils import prepare_headers
13
- from cgc.utils.consts.env_consts import API_URL, TMP_DIR
13
+ from cgc.utils.consts.env_consts import TMP_DIR
14
14
 
15
15
  from cgc.utils.requests_helper import call_api, EndpointTypes
16
16
  from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
@@ -29,9 +29,10 @@ def _get_jwt_from_server(user_id: str = None, password: str = None) -> str:
29
29
  if user_id is None or password is None:
30
30
  if user_id is not None or password is not None:
31
31
  raise ValueError("Both user_id and password must be provided")
32
+ if user_id is None and password is None:
32
33
  user_id = urllib.parse.quote(read_from_cfg("user_id"))
33
34
  password = urllib.parse.quote(read_from_cfg("password"))
34
- url = f"{API_URL}/v1/api/user/create/token"
35
+ url = f"{prepare_headers.load_user_api_url()}/v1/api/user/create/token"
35
36
  headers = {
36
37
  "accept": "application/json",
37
38
  "Content-Type": "application/x-www-form-urlencoded",
@@ -54,7 +55,7 @@ def get_jwt(user_id: str = None, password: str = None) -> str:
54
55
 
55
56
  def auth_create_api_key_with_save(user_id: str = None, password: str = None):
56
57
  """Function to create API key and API secret for user and save it to config file."""
57
- url = f"{API_URL}/v1/api/user/create/api-key"
58
+ url = f"{prepare_headers.load_user_api_url()}/v1/api/user/create/api-key"
58
59
  headers = prepare_headers.prepare_headers_api_key(user_id, password)
59
60
  metric = "auth.api-key"
60
61
  __res = call_api(
@@ -74,7 +75,7 @@ def auth_create_api_key_with_save(user_id: str = None, password: str = None):
74
75
 
75
76
  def auth_delete_api_key(api_key: str, user_id: str = None, password: str = None):
76
77
  """Function to delete API key."""
77
- url = f"{API_URL}/v1/api/user/delete/api-key?api_key={api_key}"
78
+ url = f"{prepare_headers.load_user_api_url()}/v1/api/user/delete/api-key?api_key={api_key}"
78
79
  headers = prepare_headers.prepare_headers_api_key(user_id, password)
79
80
  metric = "auth.api-key"
80
81
  __res = call_api(
@@ -89,7 +90,7 @@ def auth_delete_api_key(api_key: str, user_id: str = None, password: str = None)
89
90
 
90
91
  def auth_list_api_keys(user_id: str = None, password: str = None):
91
92
  """Function to list API keys."""
92
- url = f"{API_URL}/v1/api/user/list/api-key"
93
+ url = f"{prepare_headers.load_user_api_url()}/v1/api/user/list/api-key"
93
94
  headers = prepare_headers.prepare_headers_api_key(user_id, password)
94
95
  metric = "auth.api-key"
95
96
  __res = call_api(
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+
4
+ class CGCEntityList(Enum):
5
+ """Base class for other lists"""
6
+
7
+ @classmethod
8
+ def get_list(cls) -> list[str]:
9
+ return [el.value for el in cls]
@@ -10,7 +10,7 @@ from cgc.commands.compute.compute_responses import (
10
10
  compute_list_response,
11
11
  get_compute_port_list,
12
12
  )
13
- from cgc.commands.compute.compute_utills import (
13
+ from cgc.commands.compute.compute_utils import (
14
14
  compute_create_payload,
15
15
  port_delete_payload,
16
16
  port_modification_payload,
@@ -1,12 +1,4 @@
1
- from enum import Enum
2
-
3
-
4
- class CGCEntityList(Enum):
5
- """Base class for other lists"""
6
-
7
- @classmethod
8
- def get_list(cls) -> list[str]:
9
- return [el.value for el in cls]
1
+ from cgc.commands.cgc_models import CGCEntityList
10
2
 
11
3
 
12
4
  class ComputesList(CGCEntityList):
@@ -29,20 +21,6 @@ class ComputesList(CGCEntityList):
29
21
  CUSTOM = "custom"
30
22
 
31
23
 
32
- class DatabasesList(CGCEntityList):
33
- """List of templates in cgc-server
34
-
35
- :param Enum: name of template
36
- :type Enum: str
37
- """
38
-
39
- MONGODB = "mongodb"
40
- POSTGRESQL = "postgresql"
41
- REDIS = "redis"
42
- WEAVIATE = "weaviate"
43
- # MINIO = "minio"
44
-
45
-
46
24
  class GPUsList(CGCEntityList):
47
25
  """List of templates in cgc-server
48
26
 
@@ -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, get_app_mounts
3
+ from cgc.commands.compute.compute_utils 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
cgc/commands/db/db_cmd.py CHANGED
@@ -2,9 +2,10 @@ import json
2
2
  import click
3
3
 
4
4
  from cgc.commands.compute.compute_responses import compute_list_response
5
- from cgc.commands.compute.compute_models import DatabasesList
5
+ from cgc.commands.db.db_models import DatabasesList
6
6
  from cgc.commands.compute.compute_responses import compute_create_response
7
- from cgc.commands.compute.compute_utills import compute_create_payload
7
+ from cgc.commands.compute.compute_utils import compute_create_payload
8
+ from cgc.commands.exceptions import DatabaseCreationException
8
9
  from cgc.commands.resource.resource_cmd import resource_delete
9
10
  from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
10
11
  from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
@@ -66,6 +67,11 @@ def db_create(
66
67
  :param name: name of app
67
68
  :type name: str
68
69
  """
70
+ try:
71
+ DatabasesList.verify(entity)
72
+ except DatabaseCreationException as e:
73
+ click.echo(e, err=True, color="yellow")
74
+ return
69
75
  api_url, headers = get_api_url_and_prepare_headers()
70
76
  url = f"{api_url}/v1/api/resource/create"
71
77
  metric = "db.create"
@@ -0,0 +1,37 @@
1
+ from cgc.commands.cgc_models import CGCEntityList
2
+ from cgc.commands.exceptions import DatabaseCreationException
3
+
4
+
5
+ class DatabasesList(CGCEntityList):
6
+ """List of templates in cgc-server
7
+
8
+ :param Enum: name of template
9
+ :type Enum: str
10
+ """
11
+
12
+ POSTGRESQL = "postgresql"
13
+ WEAVIATE = "weaviate"
14
+ # MINIO = "minio"
15
+ MONGODB = "mongodb"
16
+ REDIS = "redis"
17
+
18
+ @staticmethod
19
+ def verify(entity: str) -> str:
20
+ if entity == "mongodb":
21
+ raise DatabaseCreationException(
22
+ """
23
+ Due to license agreement we can not serve MongoDB as a single click app.
24
+ If you like to use it you can spawn one as custom image.
25
+
26
+ cgc compute create custom -n name -c 4 -m 8 --image mongo
27
+ """
28
+ )
29
+ elif entity == "redis":
30
+ raise DatabaseCreationException(
31
+ """
32
+ Due to license agreement we can not serve Redis as a single click app.
33
+ If you like to use it you can spawn one as custom image.
34
+
35
+ cgc compute create custom -n name -c 4 -m 8 --image redis
36
+ """
37
+ )
@@ -1,2 +1,10 @@
1
1
  class ResponseException(Exception):
2
2
  pass
3
+
4
+
5
+ class InternalException(Exception):
6
+ pass
7
+
8
+
9
+ class DatabaseCreationException(InternalException):
10
+ pass
@@ -1,5 +1,5 @@
1
1
  from typing import Optional
2
- from cgc.commands.compute.compute_utills import list_get_mounted_volumes
2
+ from cgc.commands.compute.compute_utils import list_get_mounted_volumes
3
3
  import cgc.utils.consts.env_consts as env_consts
4
4
 
5
5
 
@@ -106,35 +106,46 @@ def job_create_payload(
106
106
  return payload
107
107
 
108
108
 
109
- def get_job_list(job_pod_list: list, job_list: list):
110
- list_of_json_data = get_job_pod_list(job_pod_list)
111
- for json_data in list_of_json_data:
112
- for job in job_list:
113
- if job.get("name") == json_data.get("name"):
114
- json_data["ttl_seconds_after_finished"] = job.get(
115
- "ttl_seconds_after_finished", "N/A"
116
- )
109
+ def get_job_list(job_list: list, job_pod_list: list):
110
+ list_of_json_job_data = get_job_json_data(job_list)
111
+
112
+ for i, job_data in enumerate(job_list):
113
+ list_of_json_job_data[i]["name"] = job_data.get("name", "")
114
+ list_of_json_job_data[i]["ttl_seconds_after_finished"] = job_data.get(
115
+ "ttl_seconds_after_finished", "N/A"
116
+ )
117
+ for job in list_of_json_job_data:
118
+ for job_pod in job_pod_list:
119
+ if job_pod.get("labels", {}).get("app-name") == job.get("name"):
120
+ job["status"] = job_pod.get("status", "Pending")
121
+ # job["status_reason"] = [
122
+ # x.get("reason", "N/A") for x in job_pod.get("status_reasons", [])
123
+ # ]
124
+ # job["status_message"] = [
125
+ # x.get("message", "N/A") for x in job_pod.get("status_reasons", [])
126
+ # ]
117
127
  break
118
- return list_of_json_data
128
+
129
+ return list_of_json_job_data
119
130
 
120
131
 
121
- def get_job_pod_list(job_pod_list: list) -> list:
132
+ def get_job_json_data(job_list: list):
122
133
  """Formats and returns list of jobs to print.
123
134
 
124
- :param pod_list: list of pods
125
- :type pod_list: list
126
- :return: formatted list of apps
135
+ :param job_list: list of jobs
136
+ :type job_list: list
137
+ :return: formatted list of jobs
127
138
  :rtype: list
128
139
  """
129
140
  output_data = []
130
141
 
131
- for pod in job_pod_list:
142
+ for job in job_list:
132
143
  try:
133
144
  main_container_name = "custom-job"
134
145
  try:
135
146
  main_container = [
136
147
  x
137
- for x in pod.get("containers", [])
148
+ for x in job.get("containers", [])
138
149
  if x.get("name") == main_container_name
139
150
  ][0]
140
151
  except IndexError:
@@ -146,29 +157,23 @@ def get_job_pod_list(job_pod_list: list) -> list:
146
157
  cpu = limits.get("cpu") if limits is not None else 0
147
158
  ram = limits.get("memory") if limits is not None else "0Gi"
148
159
 
149
- pod_data = {
150
- "name": pod.get("labels", {}).get("app-name"),
151
- "status": pod.get("status", {}),
160
+ job_data = {
161
+ "name": job.get("labels", {}).get("app-name"),
162
+ "status": job.get("status", {}).get("phase", "Pending"),
152
163
  "volumes_mounted": volumes_mounted,
153
164
  "cpu": cpu,
154
165
  "ram": ram,
155
- "gpu-count": pod.get("labels", {}).get("gpu-count", 0),
156
- "gpu-label": pod.get("labels", {}).get("gpu-label", "N/A"),
166
+ "gpu-count": job.get("labels", {}).get("gpu-count", 0),
167
+ "gpu-label": job.get("labels", {}).get("gpu-label", "N/A"),
157
168
  }
158
169
  # getting rid of unwanted and used values
159
- if "pod-template-hash" in pod["labels"].keys():
160
- pod["labels"].pop("pod-template-hash")
161
- pod["labels"].pop("app-name")
162
- pod["labels"].pop("entity")
163
- pod["labels"].pop("resource-type")
164
- pod["labels"].pop("job-name")
165
- pod["labels"].pop("controller-uid")
166
- pod["labels"].pop("api-key-id", None)
167
- pod["labels"].pop("user-id", None)
170
+ if "pod-template-hash" in job["labels"].keys():
171
+ job["labels"].pop("pod-template-hash")
172
+ job["labels"].pop("entity")
168
173
 
169
174
  # appending the rest of labels
170
- pod_data.update(pod["labels"])
171
- output_data.append(pod_data)
175
+ job_data.update(job["labels"])
176
+ output_data.append(job_data)
172
177
  except KeyError:
173
178
  pass
174
179
 
@@ -32,7 +32,7 @@ def job_list_response(data: dict) -> list:
32
32
  if not job_list:
33
33
  raise NoJobsToList()
34
34
 
35
- list_of_json_data = get_job_list(job_pod_list, job_list)
35
+ list_of_json_data = get_job_list(job_list, job_pod_list)
36
36
  table = fill_missing_values_in_a_response(list_of_json_data)
37
37
 
38
38
  return tabulate_a_response(table)
@@ -1,7 +1,8 @@
1
1
  import click
2
2
  import json
3
3
 
4
- from cgc.commands.compute.compute_models import ComputesList, DatabasesList
4
+ from cgc.commands.db.db_models import DatabasesList
5
+ from cgc.commands.compute.compute_models import ComputesList
5
6
  from cgc.commands.compute.compute_responses import (
6
7
  template_list_response,
7
8
  template_get_start_path_response,
@@ -9,7 +10,7 @@ from cgc.commands.compute.compute_responses import (
9
10
  compute_delete_response,
10
11
  )
11
12
 
12
- from cgc.commands.compute.compute_utills import compute_delete_payload
13
+ from cgc.commands.compute.compute_utils import compute_delete_payload
13
14
  from cgc.commands.resource.resource_responses import get_ingress_list_from_response
14
15
 
15
16
  from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
@@ -0,0 +1,14 @@
1
+ from cgc.commands.exceptions import ResponseException
2
+
3
+
4
+ class KeysCommandException(ResponseException):
5
+ """Base exception for all key commands."""
6
+
7
+
8
+ class SecretsCommandException(ResponseException):
9
+ """Base exception for all secret commands."""
10
+
11
+
12
+ class NoSecretsToList(SecretsCommandException):
13
+ def __init__(self) -> None:
14
+ super().__init__("No secrets to list.")
@@ -0,0 +1,179 @@
1
+ import json
2
+ import click
3
+ from typing import Optional
4
+ from cgc.commands.user.keys_responses import (
5
+ create_ssh_key_response,
6
+ delete_ssh_key_response,
7
+ list_ssh_keys_response,
8
+ update_ssh_key_response,
9
+ )
10
+ from cgc.commands.user.keys_utils import create_ssh_key_payload, update_ssh_key_payload
11
+ from cgc.commands.user.keys_models import SSHKeyTypes
12
+ from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
13
+ from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
14
+ from cgc.utils.click_group import CustomGroup, CustomCommand
15
+ from cgc.utils.requests_helper import call_api, EndpointTypes
16
+
17
+
18
+ @click.group(name="keys", cls=CustomGroup)
19
+ def keys_group():
20
+ """
21
+ Management of keys.
22
+ """
23
+
24
+
25
+ @keys_group.group(name="ssh", cls=CustomGroup)
26
+ def ssh_keys_group():
27
+ """
28
+ Management of ssh keys.
29
+ """
30
+
31
+
32
+ @ssh_keys_group.command("create", cls=CustomCommand)
33
+ @click.option(
34
+ "-pk",
35
+ "--pub-key",
36
+ "public_key",
37
+ type=click.STRING,
38
+ required=False,
39
+ help="Whole Public key string",
40
+ )
41
+ @click.option(
42
+ "-kt",
43
+ "--key-type",
44
+ "key_type",
45
+ type=click.Choice(SSHKeyTypes.get_list(), case_sensitive=False),
46
+ required=False,
47
+ # default="ssh-rsa",
48
+ help="Type of the key",
49
+ )
50
+ @click.option(
51
+ "-k",
52
+ "--key",
53
+ "key",
54
+ type=click.STRING,
55
+ required=False,
56
+ help="Public key",
57
+ )
58
+ @click.option(
59
+ "-c",
60
+ "--comment",
61
+ "comment",
62
+ type=click.STRING,
63
+ required=False,
64
+ default="",
65
+ help="Comment for the key",
66
+ )
67
+ def create_ssh_key(
68
+ public_key: Optional[str] = None,
69
+ key_type: Optional[str] = None,
70
+ key: Optional[str] = None,
71
+ comment: str = "",
72
+ ):
73
+ """Create a new SSH key"""
74
+ api_url, headers = get_api_url_and_prepare_headers()
75
+ url = f"{api_url}/v1/api/keys/ssh/create"
76
+ metric = "keys.create"
77
+ __payload = create_ssh_key_payload(public_key, key_type, key, comment)
78
+ __res = call_api(
79
+ request=EndpointTypes.post,
80
+ url=url,
81
+ headers=headers,
82
+ data=json.dumps(__payload).encode("utf-8"),
83
+ )
84
+ click.echo(
85
+ create_ssh_key_response(
86
+ retrieve_and_validate_response_send_metric(__res, metric)
87
+ ),
88
+ color="green",
89
+ )
90
+
91
+
92
+ @ssh_keys_group.command("update", cls=CustomCommand)
93
+ @click.argument("key_id", type=click.STRING)
94
+ @click.option(
95
+ "-kt",
96
+ "--key-type",
97
+ "key_type",
98
+ type=click.Choice(SSHKeyTypes.get_list(), case_sensitive=False),
99
+ # default="ssh-rsa",
100
+ required=False,
101
+ help="Type of the key",
102
+ )
103
+ @click.option(
104
+ "-k",
105
+ "--key",
106
+ "key",
107
+ type=click.STRING,
108
+ required=False,
109
+ help="Public key",
110
+ )
111
+ @click.option(
112
+ "-c",
113
+ "--comment",
114
+ "comment",
115
+ type=click.STRING,
116
+ required=False,
117
+ help="Comment for the key",
118
+ )
119
+ def update_ssh_key(
120
+ key_id: str,
121
+ key_type: Optional[str] = None,
122
+ key: Optional[str] = None,
123
+ comment: Optional[str] = None,
124
+ ):
125
+ """Update an existing SSH key"""
126
+ api_url, headers = get_api_url_and_prepare_headers()
127
+ url = f"{api_url}/v1/api/keys/ssh/update/{key_id}"
128
+ metric = "keys.update"
129
+ __payload = update_ssh_key_payload(key_type, key, comment)
130
+ __res = call_api(
131
+ request=EndpointTypes.post,
132
+ url=url,
133
+ headers=headers,
134
+ data=json.dumps(__payload).encode("utf-8"),
135
+ )
136
+ click.echo(
137
+ update_ssh_key_response(
138
+ retrieve_and_validate_response_send_metric(__res, metric)
139
+ ),
140
+ color="green",
141
+ )
142
+
143
+
144
+ @ssh_keys_group.command("delete", cls=CustomCommand)
145
+ @click.argument("key_id", type=click.STRING)
146
+ def delete_ssh_key(key_id: str):
147
+ """Delete an SSH key"""
148
+ api_url, headers = get_api_url_and_prepare_headers()
149
+ url = f"{api_url}/v1/api/keys/ssh/delete/{key_id}"
150
+ metric = "keys.delete"
151
+ __res = call_api(
152
+ request=EndpointTypes.delete,
153
+ url=url,
154
+ headers=headers,
155
+ )
156
+ click.echo(
157
+ delete_ssh_key_response(
158
+ retrieve_and_validate_response_send_metric(__res, metric)
159
+ ),
160
+ color="green",
161
+ )
162
+
163
+
164
+ @ssh_keys_group.command("list", cls=CustomCommand)
165
+ def list_ssh_keys():
166
+ """List all SSH keys"""
167
+ api_url, headers = get_api_url_and_prepare_headers()
168
+ url = f"{api_url}/v1/api/keys/ssh/list"
169
+ metric = "keys.list"
170
+ __res = call_api(
171
+ request=EndpointTypes.get,
172
+ url=url,
173
+ headers=headers,
174
+ )
175
+ click.echo(
176
+ list_ssh_keys_response(
177
+ retrieve_and_validate_response_send_metric(__res, metric)
178
+ )
179
+ )