cgcsdk 0.8.5__py3-none-any.whl → 0.8.7__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
@@ -7,4 +7,4 @@ CONFIG_FILE_NAME = cfg.json
7
7
  TMP_DIR = .tmp
8
8
  RELEASE = 0
9
9
  MAJOR_VERSION = 8
10
- MINOR_VERSION = 5
10
+ MINOR_VERSION = 7
cgc/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.8.7
4
+
5
+ Release on May 23, 2023.
6
+
7
+ * hotfix: development changed .env file making it broken for new users
8
+
9
+ ## 0.8.6
10
+
11
+ Release on May 19, 2023.
12
+
13
+ * added get_redis_client_async() for RedisConnector
14
+ * get_redis_access() - async_client: bool, new parameter
15
+ * new command: cgc context switch [number]
16
+ * switch between user contexts
17
+ * new command: cgc context list
18
+ * list all configuration files
19
+ * updated command: cgc register
20
+ * allow to have multiple user contexts
21
+ * increased timeout for requests to 30 sec
22
+
3
23
  ## 0.8.5
4
24
 
5
25
  Release on Apr 21, 2023.
cgc/cgc.py CHANGED
@@ -15,6 +15,7 @@ from cgc.commands.cgc_cmd import (
15
15
  cgc_status,
16
16
  sending_telemetry_permission,
17
17
  resource_events,
18
+ context_group,
18
19
  )
19
20
  from cgc.utils.version_control import check_version, _get_version
20
21
  from cgc.utils.click_group import CustomGroup
@@ -28,6 +29,7 @@ def cli():
28
29
 
29
30
 
30
31
  cli.add_command(debug_group)
32
+ cli.add_command(context_group)
31
33
  cli.add_command(auth_register)
32
34
  cli.add_command(api_keys_group)
33
35
  cli.add_command(volume_group)
@@ -8,3 +8,8 @@ class AuthCommandException(ResponseException):
8
8
  class NoNamespaceInConfig(AuthCommandException):
9
9
  def __init__(self) -> None:
10
10
  super().__init__(f"Namespace not readable from config file.")
11
+
12
+
13
+ class NoConfigFileFound(AuthCommandException):
14
+ def __init__(self) -> None:
15
+ super().__init__(f"Config does not exists.")
@@ -7,12 +7,16 @@ from cgc.commands.auth.auth_responses import (
7
7
  from cgc.commands.auth.auth_utils import (
8
8
  auth_create_api_key_with_save,
9
9
  )
10
-
11
10
  from cgc.utils.prepare_headers import get_url_and_prepare_headers_register
12
11
  from cgc.utils.cryptography import rsa_crypto
13
12
  from cgc.utils.click_group import CustomCommand, CustomGroup
14
13
  from cgc.utils.requests_helper import call_api, EndpointTypes
15
14
  from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
15
+ from cgc.utils import (
16
+ check_if_config_exist,
17
+ require_confirm_loop,
18
+ find_first_available_config_name,
19
+ )
16
20
 
17
21
 
18
22
  @click.group("api-keys", cls=CustomGroup, hidden=True)
@@ -24,9 +28,9 @@ def api_keys_group():
24
28
 
25
29
 
26
30
  @click.command("register", cls=CustomCommand)
27
- @click.option("--user_id", "-u", "user_id", prompt=True)
28
- @click.option("--access_key", "-k", "access_key", prompt=True)
29
- def auth_register(user_id: str, access_key: str):
31
+ # @click.option("--user_id", "-u", "user_id", prompt=True)
32
+ # @click.option("--access_key", "-k", "access_key", prompt=True)
33
+ def auth_register(config_filename: str = "cfg.json"):
30
34
  """Register a user in system using user id and access key.\n
31
35
  Enabling/Disabling Telemetry sending is available, if set to yes CGC will send
32
36
  usage metrics for application improvements purposes.
@@ -40,6 +44,13 @@ def auth_register(user_id: str, access_key: str):
40
44
  :type telemetry_sending: bool
41
45
  """
42
46
 
47
+ if check_if_config_exist(config_filename):
48
+ click.echo("Already registered.")
49
+ require_confirm_loop("Do you want to add new context?")
50
+ config_filename = find_first_available_config_name()
51
+
52
+ user_id = input("User ID: ")
53
+ access_key = input("Access key: ")
43
54
  url, headers = get_url_and_prepare_headers_register(user_id, access_key)
44
55
  metric = "auth.register"
45
56
  pub_key_bytes, priv_key_bytes = rsa_crypto.key_generate_pair()
@@ -56,6 +67,7 @@ def auth_register(user_id: str, access_key: str):
56
67
  retrieve_and_validate_response_send_metric(__res, metric, False),
57
68
  user_id,
58
69
  priv_key_bytes,
70
+ config_filename,
59
71
  )
60
72
  )
61
73
 
@@ -2,26 +2,34 @@ import shutil
2
2
  import os
3
3
 
4
4
  from cgc.commands.auth import auth_utils
5
- from cgc.utils.consts.env_consts import TMP_DIR
6
- from cgc.utils.config_utils import get_config_path, save_to_config, user_config_file
5
+ from cgc.utils.consts.env_consts import TMP_DIR, get_config_file_name
6
+ from cgc.utils.config_utils import get_config_path, save_to_config
7
7
  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) -> str:
11
+ def auth_register_response(response, user_id, priv_key_bytes, config_filename) -> str:
12
12
  TMP_DIR_PATH = os.path.join(get_config_path(), TMP_DIR)
13
- unzip_dir = auth_utils.save_and_unzip_file(response)
13
+ unzip_dir, namespace = auth_utils.save_and_unzip_file(response)
14
14
  aes_key, password = auth_utils.get_aes_key_and_password(unzip_dir, priv_key_bytes)
15
15
 
16
- save_to_config(user_id=user_id, password=password, aes_key=aes_key)
16
+ os.environ["CONFIG_FILE_NAME"] = config_filename
17
+ save_to_config(
18
+ user_id=user_id, password=password, aes_key=aes_key, namespace=namespace
19
+ )
17
20
  auth_utils.auth_create_api_key_with_save()
18
21
  shutil.rmtree(TMP_DIR_PATH)
19
- return f"Register successful! You can now use the CLI. Saved data to: {user_config_file}\n\
20
- Consider backup this file. It stores data with which you can access CGC platform."
22
+ # config.json
23
+ if config_filename == "config.json":
24
+ return f"Register successful! You can now use the CLI. Saved data to:{os.path.join(get_config_path(),config_filename)}\n\
25
+ Consider backup this file. It stores data accessible only to you with which you can access CGC platform."
26
+ return f"New context created successfully! \nNew config file saved to: {os.path.join(get_config_path(),config_filename)}\n\
27
+ Consider backup this file. It stores data accessible only to you with which you can access CGC platform.\n \n\
28
+ To switch context use \ncgc context switch"
21
29
 
22
30
 
23
31
  @key_error_decorator_for_helpers
24
32
  def login_successful_response():
25
33
  return f"Successfully logged in, created new API key pair.\n\
26
- Saved data to: {user_config_file}.\n\
27
- Consider backup this file. It stores data with which you can access CGC platform."
34
+ Saved data to: {os.path.join(get_config_path(), get_config_file_name())}.\n\
35
+ Consider backup this file. It stores data accessible only to you with which you can access CGC platform."
@@ -76,7 +76,6 @@ def save_and_unzip_file(res: requests.Response) -> str:
76
76
  """
77
77
  zip_file = res.headers.get("content-disposition").split('"')[1]
78
78
  namespace = zip_file.split("---")[-1].split(".")[0]
79
- save_to_config(namespace=namespace)
80
79
 
81
80
  if not os.path.isdir(TMP_DIR_PATH):
82
81
  os.makedirs(TMP_DIR_PATH)
@@ -87,7 +86,7 @@ def save_and_unzip_file(res: requests.Response) -> str:
87
86
  unzip_dir = zip_file_path[:-4]
88
87
  shutil.unpack_archive(zip_file_path, unzip_dir)
89
88
 
90
- return unzip_dir
89
+ return unzip_dir, namespace
91
90
 
92
91
 
93
92
  def get_aes_key_and_password(unzip_dir: str, priv_key_bytes: bytes):
cgc/commands/cgc_cmd.py CHANGED
@@ -4,11 +4,15 @@ import json
4
4
  from cgc.commands.cgc_cmd_responses import cgc_status_response
5
5
  from cgc.commands.resource.resource_cmd import resource_delete
6
6
  from cgc.utils.requests_helper import call_api, EndpointTypes
7
- from cgc.utils.click_group import CustomCommand
7
+ from cgc.utils.click_group import CustomCommand, CustomGroup
8
8
  from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
9
9
  from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
10
10
  from cgc.telemetry.basic import telemetry_permission_set
11
11
  from cgc.commands.compute.compute_responses import compute_logs_response
12
+ from cgc.commands.auth.auth_cmd import auth_register
13
+ from cgc.utils import set_environment_data, check_if_config_exist, list_all_config_files
14
+ from cgc.commands.cgc_helpers import table_of_user_context_files
15
+ from cgc.utils.config_utils import config_path
12
16
 
13
17
 
14
18
  @click.command("rm", cls=CustomCommand)
@@ -62,3 +66,52 @@ def sending_telemetry_permission():
62
66
  click.echo(
63
67
  f"Sending telemetry is now {'enabled' if telemetry_permission_set() else 'disabled'}"
64
68
  )
69
+
70
+
71
+ @click.group(name="context", cls=CustomGroup)
72
+ def context_group():
73
+ """
74
+ Switch between namespaces (contexts) that are at your disposal.
75
+ """
76
+
77
+
78
+ @context_group.command("switch", cls=CustomCommand)
79
+ @click.argument("number", type=click.INT)
80
+ # @click.pass_context
81
+ def switch_context(number: int):
82
+ """Set which namespace config should be used. After switching context your next command will be run in given namespace that corresponds to user namespace"""
83
+ file_name = f"{number}.json" if number > 1 else "cfg.json"
84
+
85
+ if not check_if_config_exist(file_name):
86
+ click.echo("Selected context does not exist.")
87
+ click.echo("To get all available contexts use:")
88
+ click.echo("cgc context list")
89
+ exit(0)
90
+ # user_id = input("User ID: ")
91
+ # access_key = input("Access key: ")
92
+ # ctx.invoke(
93
+ # auth_register,
94
+ # user_id=user_id,
95
+ # access_key=access_key,
96
+ # config_filename=file_name,
97
+ # )
98
+ set_environment_data("CONFIG_FILE_NAME", file_name)
99
+ click.echo(f"Context file changed to: {file_name}")
100
+
101
+
102
+ @context_group.command("list", cls=CustomCommand)
103
+ def list_context():
104
+ """List all namespaces available to you"""
105
+ click.echo(table_of_user_context_files(list_all_config_files()))
106
+
107
+
108
+ @context_group.command("folder", cls=CustomCommand)
109
+ def folder_of_contexts():
110
+ """Check location of config files in case that you need to export them."""
111
+ click.echo(f"All config files are located: {config_path}")
112
+ click.echo(
113
+ "If you'd like to use them on a different machine just copy all the files to corresponding folder."
114
+ )
115
+ click.echo(
116
+ "You can check location of that folder on different machine with the same command."
117
+ )
@@ -0,0 +1,33 @@
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)
cgc/sdk/redis.py CHANGED
@@ -1,7 +1,11 @@
1
- from redis import Redis, ConnectionError
1
+ from redis.asyncio import redis as redis_async
2
+ import redis
2
3
 
3
4
 
4
5
  class RedisConnector:
6
+ redis_client_async = None
7
+ redis_client = None
8
+
5
9
  def __init__(
6
10
  self, host: str, password: str = None, decode_responses: bool = False
7
11
  ) -> None:
@@ -10,42 +14,75 @@ class RedisConnector:
10
14
  "host must be a str containing redis app name"
11
15
  self._password = password
12
16
  self._decode_responses = decode_responses
13
- self.connect()
14
17
 
15
- def connect(self):
18
+ def connect(self, async_client: bool = False):
16
19
  while True:
17
20
  try:
18
- self._redis_client = Redis(
19
- host=self._host,
20
- port=6379,
21
- password=self._password,
22
- decode_responses=self._decode_responses,
23
- )
21
+ if not async_client:
22
+ self.redis_client = redis.Redis(
23
+ host=self._host,
24
+ port=6379,
25
+ password=self._password,
26
+ decode_responses=self._decode_responses,
27
+ )
28
+ else:
29
+ self.redis_client_async = redis_async.Redis(
30
+ host=self._host,
31
+ port=6379,
32
+ password=self._password,
33
+ decode_responses=self._decode_responses,
34
+ )
24
35
  print(f"Connected to Redis: {self._host}")
25
36
  break
26
- except (ConnectionError,) as e:
37
+ except (redis.ConnectionError,) as e:
27
38
  print(f"Redis connection error: {e}")
28
39
  print(f"retrying to connect...")
29
40
 
30
41
  def get_redis_client(self):
31
- return self._redis_client
42
+ if self.redis_client is None:
43
+ self.connect()
44
+ return self.redis_client
45
+
46
+ def get_redis_client_async(self):
47
+ if self.redis_client_async is None:
48
+ self.connect(async_client=True)
49
+ return self.redis_client_async
32
50
 
33
51
 
34
52
  def get_redis_access(
35
- app_name: str, password: str, decode_responses: bool = False, restart: bool = False
53
+ app_name: str,
54
+ password: str,
55
+ decode_responses: bool = False,
56
+ restart: bool = False,
57
+ async_client=False,
36
58
  ):
37
59
  global _redis_access
60
+ global _redis_access_async
38
61
 
39
- def init_access():
62
+ def init_access(async_client=False):
40
63
  global _redis_access
41
- _redis_access = RedisConnector(
42
- host=app_name, password=password, decode_responses=decode_responses
43
- )
64
+ global _redis_access_async
65
+
66
+ if not async_client:
67
+ _redis_access = RedisConnector(
68
+ host=app_name, password=password, decode_responses=decode_responses
69
+ )
70
+ else:
71
+ _redis_access_async = RedisConnector(
72
+ host=app_name, password=password, decode_responses=decode_responses
73
+ )
44
74
 
45
75
  try:
46
- if not isinstance(_redis_access, RedisConnector) or restart:
47
- init_access()
76
+ if not async_client:
77
+ if not isinstance(_redis_access, RedisConnector) or restart:
78
+ init_access()
79
+ else:
80
+ if not isinstance(_redis_access_async, RedisConnector) or restart:
81
+ init_access(True)
48
82
  except NameError:
49
- init_access()
83
+ if not async_client:
84
+ init_access()
85
+ else:
86
+ init_access(True)
50
87
  pass
51
- return _redis_access
88
+ return _redis_access if async_client else _redis_access_async
cgc/utils/__init__.py CHANGED
@@ -0,0 +1,113 @@
1
+ from typing import List
2
+ from os import listdir
3
+ from os.path import isfile, join
4
+ from cgc.utils.config_utils import config_path
5
+ from operator import is_not
6
+ from functools import partial
7
+ from random import randrange
8
+
9
+ from cgc.utils.consts.env_consts import ENV_FILE_PATH
10
+ from cgc.utils.config_utils import read_from_cfg
11
+ from cgc.commands.auth import NoConfigFileFound
12
+
13
+
14
+ def require_confirm_loop(message: str):
15
+ while True:
16
+ answer = input(f"{message} (Y/N): ").lower()
17
+ if answer in ("y", "yes"):
18
+ break
19
+ if answer in ("n", "no"):
20
+ exit(0)
21
+
22
+
23
+ def quick_sort(collection: list) -> list:
24
+ """A pure Python implementation of quick sort algorithm
25
+
26
+ :param collection: a mutable collection of comparable items
27
+ :return: the same collection ordered by ascending
28
+
29
+ Examples:
30
+ >>> quick_sort([0, 5, 3, 2, 2])
31
+ [0, 2, 2, 3, 5]
32
+ >>> quick_sort([])
33
+ []
34
+ >>> quick_sort([-2, 5, 0, -45])
35
+ [-45, -2, 0, 5]
36
+ """
37
+ if len(collection) < 2:
38
+ return collection
39
+ pivot_index = randrange(len(collection)) # Use random element as pivot
40
+ pivot = collection[pivot_index]
41
+ greater: list[int] = [] # All elements greater than pivot
42
+ lesser: list[int] = [] # All elements less than or equal to pivot
43
+
44
+ for element in collection[:pivot_index]:
45
+ (greater if element > pivot else lesser).append(element)
46
+
47
+ for element in collection[pivot_index + 1 :]:
48
+ (greater if element > pivot else lesser).append(element)
49
+
50
+ return [*quick_sort(lesser), pivot, *quick_sort(greater)]
51
+
52
+
53
+ def set_environment_data(variable: str, value: str):
54
+ """Set variable to .env file
55
+
56
+ :return: new value
57
+ :rtype: str
58
+ """
59
+ f = open(file=ENV_FILE_PATH, mode="r")
60
+ replaced_content = f.read()
61
+ replaced_content = replaced_content.splitlines()
62
+ f.close()
63
+ for i, line in enumerate(replaced_content):
64
+ splitted = line.split(" ")
65
+ if splitted[0] == variable.upper():
66
+ replaced_content[i] = line.replace(splitted[2], value)
67
+ with open(file=ENV_FILE_PATH, mode="w") as f:
68
+ f.write("\n".join(replaced_content))
69
+ break
70
+ else:
71
+ with open(file=ENV_FILE_PATH, mode="a") as f:
72
+ f.write(f"\n{variable.upper()} = {value}")
73
+
74
+ return value
75
+
76
+
77
+ def find_first_available_config_name() -> str:
78
+ increment = 2
79
+ while True:
80
+ filename = f"{increment}.json"
81
+ try:
82
+ read_from_cfg(None, filename)
83
+ increment += 1
84
+ except NoConfigFileFound:
85
+ break
86
+ return filename
87
+
88
+
89
+ def check_if_config_exist(filename: str) -> bool:
90
+ try:
91
+ read_from_cfg(None, filename)
92
+ except NoConfigFileFound:
93
+ return False
94
+ return True
95
+
96
+
97
+ def list_all_config_files() -> List[str]:
98
+ only_files = [f for f in listdir(config_path) if isfile(join(config_path, f))]
99
+
100
+ def only_json_file(filename: str):
101
+ return filename if filename.endswith(".json") else None
102
+
103
+ def cgc_config_file(filename: str):
104
+ filename_prefix = filename.split(".")[0]
105
+ try:
106
+ int(filename_prefix)
107
+ return filename
108
+ except ValueError:
109
+ if filename_prefix == "cfg":
110
+ return filename
111
+
112
+ json_files = list(filter(partial(is_not, None), map(only_json_file, only_files)))
113
+ return list(filter(partial(is_not, None), map(cgc_config_file, json_files)))
cgc/utils/config_utils.py CHANGED
@@ -3,11 +3,9 @@ import os
3
3
  import sys
4
4
  import click
5
5
 
6
- from cgc.commands.auth import NoNamespaceInConfig
6
+ from cgc.commands.auth import NoNamespaceInConfig, NoConfigFileFound
7
7
  from cgc.utils.message_utils import prepare_error_message
8
- from cgc.utils.consts.env_consts import (
9
- CONFIG_FILE_NAME,
10
- )
8
+ from cgc.utils.consts.env_consts import get_config_file_name
11
9
 
12
10
 
13
11
  def get_config_path():
@@ -32,7 +30,6 @@ def get_config_path():
32
30
 
33
31
 
34
32
  config_path = get_config_path()
35
- user_config_file = os.path.join(config_path, CONFIG_FILE_NAME)
36
33
 
37
34
 
38
35
  def save_to_config(**kwargs):
@@ -44,6 +41,7 @@ def save_to_config(**kwargs):
44
41
  :type kwargs: dict
45
42
  """
46
43
  read_cfg = {}
44
+ user_config_file = os.path.join(config_path, get_config_file_name())
47
45
  if not os.path.isdir(config_path):
48
46
  os.makedirs(config_path)
49
47
  try:
@@ -57,7 +55,7 @@ def save_to_config(**kwargs):
57
55
  json.dump(final_cfg, f)
58
56
 
59
57
 
60
- def read_from_cfg(key: str):
58
+ def read_from_cfg(key: str, filename=None):
61
59
  """Function to read a single value from config
62
60
 
63
61
  :param key: key name to read the value from config
@@ -65,15 +63,18 @@ def read_from_cfg(key: str):
65
63
  :return: value for the provided key
66
64
  :rtype: _type_
67
65
  """
66
+ if filename is None:
67
+ filename_with_path = os.path.join(config_path, get_config_file_name())
68
+ else:
69
+ filename_with_path = os.path.join(config_path, filename)
68
70
  try:
69
- f = open(user_config_file, "r+", encoding="UTF-8")
70
- read_cfg = json.load(f)
71
- return read_cfg[key]
71
+ with open(filename_with_path, "r+", encoding="UTF-8") as f:
72
+ read_cfg = json.load(f)
73
+ if key is None:
74
+ return read_cfg
75
+ return read_cfg[key]
72
76
  except FileNotFoundError:
73
- if key == "namespace":
74
- raise NoNamespaceInConfig()
75
- print("No config file found. Please use cgc register first.")
76
- sys.exit()
77
+ raise NoConfigFileFound()
77
78
  except KeyError:
78
79
  if key == "namespace":
79
80
  raise NoNamespaceInConfig()
@@ -36,8 +36,11 @@ else:
36
36
 
37
37
  API_PORT = os.getenv("API_PORT")
38
38
  API_URL = f"{__prefix}://{API_HOST}:{API_PORT}"
39
- CONFIG_FILE_NAME = os.getenv("CONFIG_FILE_NAME")
40
39
  TMP_DIR = os.getenv("TMP_DIR")
41
40
  RELEASE = int(os.getenv("RELEASE"))
42
41
  MAJOR_VERSION = int(os.getenv("MAJOR_VERSION"))
43
42
  MINOR_VERSION = int(os.getenv("MINOR_VERSION"))
43
+
44
+
45
+ def get_config_file_name():
46
+ return os.getenv("CONFIG_FILE_NAME")
@@ -1,3 +1,6 @@
1
+ CONFIG_FILE_NOT_FOUND = (
2
+ "Config file not found. Please register with CGC or verify user context."
3
+ )
1
4
  UNKNOWN_ERROR = "An unknown error occurred. Please try again or contact support at support@comtegra.pl."
2
5
  TIMEOUT_ERROR = (
3
6
  "Connection timed out. Try again or contact support at support@comtegra.pl"
@@ -12,6 +15,6 @@ OUTDATED_MINOR = "There is a new release available, consider updating the applic
12
15
  OUTDATED_MAJOR = (
13
16
  "You are using outdated version of cgcsdk, please update to the latest version."
14
17
  )
15
- UNAUTHORIZED_ERROR = "Unauthorized. Please check your API key and secret key or contact support at support@comtegra.pl"
18
+ UNAUTHORIZED_ERROR = "Unauthorized. Credentials provided are invalid. Contact support at support@comtegra.pl"
16
19
  DISABLED_ERROR = "Account has been disabled. If you require assistance, please contact support at support@comtegra.pl"
17
20
  ENDPOINT_DISABLED = "Endpoint is currently disabled, please try again later or contact support for additional information at support@comtegra.pl"
@@ -1,9 +1,12 @@
1
+ import click
2
+
1
3
  from colorama import Fore, Style
2
4
  from functools import wraps
3
5
 
4
6
  from cgc.commands.exceptions import ResponseException
5
7
  from cgc.telemetry.basic import increment_metric
6
- from cgc.utils.consts.message_consts import UNKNOWN_ERROR
8
+ from cgc.utils.consts.message_consts import UNKNOWN_ERROR, CONFIG_FILE_NOT_FOUND
9
+ from cgc.commands.auth import NoConfigFileFound
7
10
 
8
11
 
9
12
  def prepare_error_message(message: str) -> str:
@@ -46,6 +49,9 @@ def key_error_decorator_for_helpers(func):
46
49
  def wrapper(*args, **kwargs):
47
50
  try:
48
51
  return func(*args, **kwargs)
52
+ except NoConfigFileFound as err:
53
+ click.echo(prepare_warning_message(CONFIG_FILE_NOT_FOUND))
54
+ exit(1)
49
55
  except (TypeError, KeyError, IndexError) as err:
50
56
  print(args, "\n", kwargs)
51
57
  increment_metric("client.parser", True)
@@ -1,6 +1,7 @@
1
1
  from cgc.utils.config_utils import read_from_cfg
2
2
  from cgc.utils.consts.env_consts import API_URL, CGC_SECRET
3
3
  from cgc.commands.auth import auth_utils
4
+ from cgc.utils.message_utils import key_error_decorator_for_helpers
4
5
 
5
6
 
6
7
  def load_user_api_keys():
@@ -12,6 +13,7 @@ def load_user_api_keys():
12
13
  return read_from_cfg("api_key"), read_from_cfg("api_secret")
13
14
 
14
15
 
16
+ @key_error_decorator_for_helpers
15
17
  def get_api_url_and_prepare_headers():
16
18
  """Loads API_URL and user api keys into single function. Ment to be used as single point of truth for all andpoints except register - due to different Content-Type header
17
19
 
@@ -53,6 +55,7 @@ def get_url_and_headers_jwt_token():
53
55
  return url, headers
54
56
 
55
57
 
58
+ @key_error_decorator_for_helpers
56
59
  def prepare_headers_api_key():
57
60
  """Prepares headers for create API key request.
58
61
 
@@ -26,7 +26,7 @@ class EndpointTypes(Enum):
26
26
 
27
27
  def _process_endpoint_kwargs(**kwargs):
28
28
  if not "timeout" in kwargs.keys():
29
- kwargs["timeout"] = 10
29
+ kwargs["timeout"] = 30
30
30
  return kwargs
31
31
 
32
32
 
@@ -4,7 +4,7 @@ import pprint
4
4
  import requests
5
5
  import json
6
6
  from tabulate import tabulate
7
- from cgc.commands.auth import NoNamespaceInConfig
7
+ from cgc.commands.auth import NoNamespaceInConfig, NoConfigFileFound
8
8
  from cgc.utils.message_utils import prepare_error_message
9
9
  from cgc.utils.consts.message_consts import (
10
10
  UNKNOWN_ERROR,
@@ -38,6 +38,10 @@ def retrieve_and_validate_response_send_metric(
38
38
  metric = f"{get_namespace()}.{metric}"
39
39
  except NoNamespaceInConfig:
40
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}"
41
45
 
42
46
  if response.status_code == 200:
43
47
  increment_metric(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cgcsdk
3
- Version: 0.8.5
3
+ Version: 0.8.7
4
4
  Summary: Comtegra GPU Cloud REST API client
5
5
  Author: Comtegra AI Team
6
6
  Author-email: ai@comtegra.pl
@@ -1,17 +1,18 @@
1
- cgc/.env,sha256=ZVNNvvvC_Iq0S7_oFnH3eXqQ9ClwRs5bYnVbIZjdreY,234
2
- cgc/CHANGELOG.md,sha256=psisninwYdabTEy8-CX4y7yb8v0CAaAL8dPjDATrT6M,3518
1
+ cgc/.env,sha256=s-vHJroAqqbjxntSwKabvrteilxs6Jb3ezMPRoIi-z8,234
2
+ cgc/CHANGELOG.md,sha256=SjMZBYWSaCASprwbfZtlNnXnSy14NbK79nRdyzNDP1s,4029
3
3
  cgc/__init__.py,sha256=d03Xv8Pw4ktNyUHfmicP6XfxYPXnVYLaCZPyUlg_RNQ,326
4
- cgc/cgc.py,sha256=mQT2tG-aKt1gpzk-DqRRdtzf1RJLdzQaRslHIrldG8M,1327
4
+ cgc/cgc.py,sha256=kPLg3h-3kjlMBiwZGOM7yvXJ7pzkVglAbWWQ7KX8jeY,1377
5
5
  cgc/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  cgc/server.crt,sha256=OEvi0e8A3Dl5NnVqxFSu1D2JhLfH79lrW_lIyNW-BVM,1452
7
7
  cgc/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- cgc/commands/cgc_cmd.py,sha256=2lm-mrKiZGJo8xMovBH1seBNn37wqyx8ycZoF3LOmBI,2031
8
+ cgc/commands/cgc_cmd.py,sha256=Tvjr_dtRur_5G-rhLFgvNNS-T19bI3bRHQ0mDbjmwLw,4061
9
9
  cgc/commands/cgc_cmd_responses.py,sha256=ToPjvGf3zUbFLhLwv-BV8ZAvAHg1xS99iprM928MnOY,2026
10
+ cgc/commands/cgc_helpers.py,sha256=i5M0xYwEAYFKeA9Rvp5x3xYBGrxtUPU8y8uegt6VkhY,1207
10
11
  cgc/commands/exceptions.py,sha256=l3Sms3D2fxSpLQQQEYeLWxO3to82myTQ0VFgFYdQdLU,45
11
- cgc/commands/auth/__init__.py,sha256=X0-Ck048htx4gOIAqjXEc3m8jFHUjc5aoww62wSe7jE,265
12
- cgc/commands/auth/auth_cmd.py,sha256=NSmi0jR3ORoYEdYT8mRAEIje6Te6HihsapPJWzZSqHA,2384
13
- cgc/commands/auth/auth_responses.py,sha256=1_NKZdrUXf3oF2OTpPIbSx6FUMEN_8D9X3G2l3lW1zI,1202
14
- cgc/commands/auth/auth_utils.py,sha256=b3e7Tt1EGbqqqOxfBKEiW-VsyMaOXdRcWS1DZIC7q0c,3922
12
+ cgc/commands/auth/__init__.py,sha256=K8HkHHotMnK7SQRAst5rx_wprHEphPo_w2KToEymjAY,399
13
+ cgc/commands/auth/auth_cmd.py,sha256=IymOWji_m_3ieuLdzaIy7EroXhmTjtVJvHN0ALivvCY,2824
14
+ cgc/commands/auth/auth_responses.py,sha256=Z1eMM_BxHZb_3fdkQobMOlOHtEG9pxfLm8D-WKbevto,1783
15
+ cgc/commands/auth/auth_utils.py,sha256=vYVoz6VOGDCkEz2CLg8Cz6voTPE9G6lEbD7RIwlaVwg,3893
15
16
  cgc/commands/billing/__init__.py,sha256=0arQm0R3Ouw7tXPooJsvWd_pGeHhzaVwQCbWMKQPT9A,753
16
17
  cgc/commands/billing/billing_cmd.py,sha256=I4OWos6DWM8zM28nJzaM8-KJhkQTPhI7oWBivIaPmKY,3450
17
18
  cgc/commands/billing/billing_responses.py,sha256=HAD5N-Odx3Jz1OmhO4v66rHoXpTYIOGlXDsrs0da9dk,1949
@@ -36,7 +37,7 @@ cgc/sdk/__init__.py,sha256=XhdIQ-K0mFVl3m4UPftBc7ZYDSsrPpLi2g2Scbk9XI0,194
36
37
  cgc/sdk/handlers.py,sha256=ECCHNe1pErsXFlmwHewsWRvYqzAZ5j5TrSqwernpLJk,868
37
38
  cgc/sdk/mongodb.py,sha256=TJ2XU7nilNRXLOIpQQPrRiVxHN2TaVM5QOSuMRtNDVs,7221
38
39
  cgc/sdk/postgresql.py,sha256=ziXaMMwjSF3k1OAID3F9npqWVxreQaoZ8wn7X8x1FZw,1637
39
- cgc/sdk/redis.py,sha256=zM5gyRH1f8UwrstChXY9D-DwmzfdvmkwvLXAHewSbus,1499
40
+ cgc/sdk/redis.py,sha256=EkVQnmxpkkofL7TsU_hqttA14PtkE4bgNPEf_cV61F0,2786
40
41
  cgc/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
42
  cgc/telemetry/basic.py,sha256=XagCcyH4QSEPmfiQ1WCjqXslnJO6IaJCY0AMPySd5rc,3175
42
43
  cgc/tests/__init__.py,sha256=NTtosBQi5W7yDpElH1B8Nim9WpkNkcqikVQXP6yWiQw,102744
@@ -49,26 +50,26 @@ cgc/tests/desired_responses/test_compute_list.txt,sha256=3CDAuILJdwBmX6-GLj3zDbn
49
50
  cgc/tests/desired_responses/test_compute_list_no_labels.txt,sha256=-OeQIaEHHsHZ81tCOI5j6VQYUMlj8VYcyyOU_DhPOpU,155
50
51
  cgc/tests/desired_responses/test_tabulate_response.txt,sha256=beNyCTS9fwrHn4ueEOVk2BpOeSYZWumIa3H5EUGnW1I,789
51
52
  cgc/tests/desired_responses/test_volume_list.txt,sha256=vYB1p50BBHD801q7LUdDc_aca4ezQ8CFLWw7I-b4Uao,309
52
- cgc/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ cgc/utils/__init__.py,sha256=h6AyoMfMxgDhvzUJ94bs3NCYzAKR2tBPG7V7u3Hl5zE,3405
53
54
  cgc/utils/click_group.py,sha256=Scfw8eMIyt2dE1ezUq2JuiI-E_LklqXQXJEr7L-EG6A,633
54
- cgc/utils/config_utils.py,sha256=qSQWTVWL4aBV41-qUipoI4juf8Bm8uqFHcf3th7CKaE,2560
55
+ cgc/utils/config_utils.py,sha256=VFTrcN9QeOByIobhh48TfAm-BWON4PN8zX0H8PdUNTU,2729
55
56
  cgc/utils/custom_exceptions.py,sha256=DXhEjei0dW-dHOSCZitiuzBQEhDSBheGBHi6gZ-jO3Q,2067
56
- cgc/utils/message_utils.py,sha256=Nh7ohAHp039rSgpC7YUMj0vR-7Kgvsh2r-zP1Qz4opk,1556
57
- cgc/utils/prepare_headers.py,sha256=FAyA6Dg4Jl5iSjtIcXiDAMiI47ZOq3LTff-XNQyMEp0,2354
58
- cgc/utils/requests_helper.py,sha256=_xa6LtKbSRMAMiUSzlwekmcMCE24lhN65BIIiolWOEE,1973
59
- cgc/utils/response_utils.py,sha256=U0v61ssYhkFxbm_eS_Dj244kLztYJHqmzXltN9t69vE,4727
57
+ cgc/utils/message_utils.py,sha256=_cTvf--X7_S740wYX91kiS1O2MO6hExJHGI1BdzGlWA,1773
58
+ cgc/utils/prepare_headers.py,sha256=gEN2UHCzEZc7T8rihOAvxdRYOyEN2BT0bRHR5CcY584,2488
59
+ cgc/utils/requests_helper.py,sha256=stXD1rRDN8-OM8Hsp516or4p-E7Gc0h4RJmvOmklSHU,1973
60
+ cgc/utils/response_utils.py,sha256=mH7Scc8aqticD17j_Sxx0WL-8PFqUhFH8Aoq-b6EGno,4913
60
61
  cgc/utils/update.py,sha256=AsQwhcBqsjgNPKn6AN6ojt0Ew5otvJXyshys6bjr7DQ,413
61
62
  cgc/utils/version_control.py,sha256=hTHb06QfzBQNmiiSBn3TUdp6JMg6d8l1Qd3y78RuSfw,2959
62
63
  cgc/utils/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
- cgc/utils/consts/env_consts.py,sha256=SuxNB1JgBNxF2vZs7b7Igverk1s0TDVvsEdwJsqurqg,1318
64
- cgc/utils/consts/message_consts.py,sha256=4u_XHfvsnDXOROwd7BD2lLgrOmo8v-zJEqFO7tQjV78,1122
64
+ cgc/utils/consts/env_consts.py,sha256=RawfOqxqSxWTU50Y2Y34CN0YDBtPMYDI_ZIKlNuPkv4,1340
65
+ cgc/utils/consts/message_consts.py,sha256=8CIe3N_HL6Pj-gSArkPkpegsvm-QMWxqqnSgtzG08Qw,1218
65
66
  cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
67
  cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
67
68
  cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
68
69
  cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
69
- cgcsdk-0.8.5.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- cgcsdk-0.8.5.dist-info/METADATA,sha256=1zKlsd2HOp7rdD_f8vvv4IOSfpgS1ezzdmXSp7AxjBw,1420
71
- cgcsdk-0.8.5.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
72
- cgcsdk-0.8.5.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
73
- cgcsdk-0.8.5.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
74
- cgcsdk-0.8.5.dist-info/RECORD,,
70
+ cgcsdk-0.8.7.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ cgcsdk-0.8.7.dist-info/METADATA,sha256=OHf5kc8nmVI4svt4s2m55o479xidCHa-KU7QRudMZIA,1420
72
+ cgcsdk-0.8.7.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
73
+ cgcsdk-0.8.7.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
74
+ cgcsdk-0.8.7.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
75
+ cgcsdk-0.8.7.dist-info/RECORD,,
File without changes