thestage 0.6.2__py3-none-any.whl → 0.6.4__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 (177) hide show
  1. thestage/.env +4 -5
  2. thestage/__init__.py +3 -3
  3. thestage/__main__.py +9 -9
  4. thestage/cli_command.py +56 -56
  5. thestage/cli_command_helper.py +51 -51
  6. thestage/color_scheme/color_scheme.py +7 -7
  7. thestage/config/__init__.py +18 -18
  8. thestage/config/config_storage.py +5 -5
  9. thestage/config/env_base.py +7 -7
  10. thestage/controllers/__init__.py +0 -0
  11. thestage/controllers/base_controller.py +67 -67
  12. thestage/controllers/config_controller.py +137 -137
  13. thestage/controllers/container_controller.py +389 -389
  14. thestage/controllers/instance_controller.py +183 -183
  15. thestage/controllers/project_controller.py +810 -810
  16. thestage/controllers/utils_controller.py +32 -32
  17. thestage/debug_main.dist.py +28 -28
  18. thestage/entities/__init__.py +0 -0
  19. thestage/entities/container.py +17 -17
  20. thestage/entities/enums/__init__.py +0 -0
  21. thestage/entities/enums/order_direction_type.py +6 -6
  22. thestage/entities/enums/shell_type.py +7 -7
  23. thestage/entities/enums/tail_output_type.py +6 -6
  24. thestage/entities/enums/yes_no_response.py +7 -7
  25. thestage/entities/file_item.py +27 -27
  26. thestage/entities/project_inference_simulator.py +18 -18
  27. thestage/entities/project_inference_simulator_model.py +17 -17
  28. thestage/entities/project_task.py +19 -19
  29. thestage/entities/rented_instance.py +19 -19
  30. thestage/entities/self_hosted_instance.py +18 -18
  31. thestage/exceptions/__init__.py +0 -0
  32. thestage/exceptions/auth_exception.py +6 -6
  33. thestage/exceptions/base_exception.py +13 -13
  34. thestage/exceptions/business_logic_exception.py +6 -6
  35. thestage/exceptions/config_exception.py +6 -6
  36. thestage/exceptions/file_system_exception.py +6 -6
  37. thestage/exceptions/git_access_exception.py +17 -17
  38. thestage/exceptions/remote_server_exception.py +24 -24
  39. thestage/git/ProgressPrinter.py +22 -22
  40. thestage/helpers/__init__.py +0 -0
  41. thestage/helpers/error_handler.py +115 -115
  42. thestage/helpers/exception_hook.py +14 -14
  43. thestage/helpers/logger/__init__.py +0 -0
  44. thestage/helpers/logger/app_logger.py +50 -50
  45. thestage/helpers/ssh_util.py +38 -38
  46. thestage/i18n/en_GB/messages.po +947 -947
  47. thestage/i18n/translation.py +9 -9
  48. thestage/main.py +36 -36
  49. thestage/services/.env +6 -6
  50. thestage/services/__init__.py +0 -0
  51. thestage/services/abstract_mapper.py +9 -9
  52. thestage/services/abstract_service.py +87 -87
  53. thestage/services/app_config_service.py +52 -52
  54. thestage/services/clients/__init__.py +0 -0
  55. thestage/services/clients/git/__init__.py +0 -0
  56. thestage/services/clients/git/git_client.py +436 -436
  57. thestage/services/clients/thestage_api/__init__.py +0 -0
  58. thestage/services/clients/thestage_api/api_client.py +718 -718
  59. thestage/services/clients/thestage_api/core/api_client_core.py +108 -108
  60. thestage/services/clients/thestage_api/core/http_client_exception.py +12 -12
  61. thestage/services/clients/thestage_api/dtos/__init__.py +0 -0
  62. thestage/services/clients/thestage_api/dtos/base_response.py +13 -13
  63. thestage/services/clients/thestage_api/dtos/cloud_provider_region.py +19 -19
  64. thestage/services/clients/thestage_api/dtos/container_param_request.py +11 -11
  65. thestage/services/clients/thestage_api/dtos/container_response.py +67 -67
  66. thestage/services/clients/thestage_api/dtos/docker_container_assigned_device.py +10 -10
  67. thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +13 -13
  68. thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_response.py +13 -13
  69. thestage/services/clients/thestage_api/dtos/docker_container_mapping.py +10 -10
  70. thestage/services/clients/thestage_api/dtos/entity_filter_request.py +14 -14
  71. thestage/services/clients/thestage_api/dtos/enums/__init__.py +0 -0
  72. thestage/services/clients/thestage_api/dtos/enums/container_pending_action.py +10 -10
  73. thestage/services/clients/thestage_api/dtos/enums/container_status.py +17 -17
  74. thestage/services/clients/thestage_api/dtos/enums/cpu_type.py +8 -8
  75. thestage/services/clients/thestage_api/dtos/enums/currency_type.py +10 -10
  76. thestage/services/clients/thestage_api/dtos/enums/daemon_status.py +9 -9
  77. thestage/services/clients/thestage_api/dtos/enums/disk_type.py +7 -7
  78. thestage/services/clients/thestage_api/dtos/enums/drive_type.py +7 -7
  79. thestage/services/clients/thestage_api/dtos/enums/gpu_name.py +8 -8
  80. thestage/services/clients/thestage_api/dtos/enums/inference_model_status.py +9 -9
  81. thestage/services/clients/thestage_api/dtos/enums/inference_simulator_status.py +15 -15
  82. thestage/services/clients/thestage_api/dtos/enums/instance_rented_status.py +17 -17
  83. thestage/services/clients/thestage_api/dtos/enums/instance_type.py +7 -7
  84. thestage/services/clients/thestage_api/dtos/enums/location_region.py +11 -11
  85. thestage/services/clients/thestage_api/dtos/enums/power_status.py +10 -10
  86. thestage/services/clients/thestage_api/dtos/enums/provider_name.py +11 -11
  87. thestage/services/clients/thestage_api/dtos/enums/selfhosted_status.py +10 -10
  88. thestage/services/clients/thestage_api/dtos/enums/task_execution_status.py +12 -12
  89. thestage/services/clients/thestage_api/dtos/enums/task_status.py +12 -12
  90. thestage/services/clients/thestage_api/dtos/frontend_status.py +10 -10
  91. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +13 -13
  92. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +13 -13
  93. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +12 -12
  94. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_response.py +12 -12
  95. thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +10 -10
  96. thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_response.py +13 -13
  97. thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_request.py +14 -14
  98. thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_response.py +12 -12
  99. thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +12 -12
  100. thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_response.py +13 -13
  101. thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +11 -11
  102. thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +11 -11
  103. thestage/services/clients/thestage_api/dtos/installed_service.py +17 -17
  104. thestage/services/clients/thestage_api/dtos/instance_detected_gpus.py +20 -20
  105. thestage/services/clients/thestage_api/dtos/instance_rented_response.py +71 -71
  106. thestage/services/clients/thestage_api/dtos/logging_controller/docker_container_log_stream_request.py +7 -7
  107. thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +13 -13
  108. thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_response.py +14 -14
  109. thestage/services/clients/thestage_api/dtos/logging_controller/task_log_stream_request.py +7 -7
  110. thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +21 -21
  111. thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_response.py +14 -14
  112. thestage/services/clients/thestage_api/dtos/paginated_entity_list.py +11 -11
  113. thestage/services/clients/thestage_api/dtos/pagination_data.py +10 -10
  114. thestage/services/clients/thestage_api/dtos/price_definition.py +14 -14
  115. thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +7 -7
  116. thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_response.py +10 -10
  117. thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +8 -8
  118. thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_response.py +6 -6
  119. thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +15 -15
  120. thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_response.py +10 -10
  121. thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +13 -13
  122. thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_response.py +10 -10
  123. thestage/services/clients/thestage_api/dtos/project_response.py +32 -32
  124. thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +56 -56
  125. thestage/services/clients/thestage_api/dtos/sftp_path_helper.py +13 -13
  126. thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_request.py +8 -8
  127. thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +11 -11
  128. thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +8 -8
  129. thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_response.py +11 -11
  130. thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_request.py +7 -7
  131. thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +12 -12
  132. thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +10 -10
  133. thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_response.py +12 -12
  134. thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +9 -9
  135. thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +12 -12
  136. thestage/services/clients/thestage_api/dtos/user_controller/user_profile.py +12 -12
  137. thestage/services/clients/thestage_api/dtos/validate_token_response.py +11 -11
  138. thestage/services/config_provider/__init__.py +0 -0
  139. thestage/services/config_provider/config_provider.py +237 -237
  140. thestage/services/connect/connect_service.py +193 -196
  141. thestage/services/connect/dto/remote_server_config.py +9 -9
  142. thestage/services/container/__init__.py +0 -0
  143. thestage/services/container/container_service.py +374 -374
  144. thestage/services/container/mapper/__init__.py +0 -0
  145. thestage/services/container/mapper/container_mapper.py +30 -30
  146. thestage/services/core_files/config_entity.py +26 -26
  147. thestage/services/filesystem_service.py +133 -133
  148. thestage/services/instance/__init__.py +0 -0
  149. thestage/services/instance/instance_service.py +303 -303
  150. thestage/services/instance/mapper/__init__.py +0 -0
  151. thestage/services/instance/mapper/instance_mapper.py +24 -24
  152. thestage/services/instance/mapper/selfhosted_mapper.py +33 -33
  153. thestage/services/logging/byte_print_style.py +5 -5
  154. thestage/services/logging/dto/log_message.py +15 -15
  155. thestage/services/logging/dto/log_type.py +6 -6
  156. thestage/services/logging/exception/log_polling_exception.py +6 -6
  157. thestage/services/logging/logging_constants.py +3 -3
  158. thestage/services/logging/logging_service.py +367 -367
  159. thestage/services/project/__init__.py +0 -0
  160. thestage/services/project/dto/inference_simulator_dto.py +22 -22
  161. thestage/services/project/dto/inference_simulator_model_dto.py +20 -20
  162. thestage/services/project/dto/project_config.py +14 -14
  163. thestage/services/project/mapper/__init__.py +0 -0
  164. thestage/services/project/mapper/project_inference_simulator_mapper.py +21 -21
  165. thestage/services/project/mapper/project_inference_simulator_model_mapper.py +21 -21
  166. thestage/services/project/mapper/project_task_mapper.py +22 -22
  167. thestage/services/project/project_service.py +1253 -1253
  168. thestage/services/remote_server_service.py +609 -609
  169. thestage/services/service_factory.py +97 -97
  170. thestage/services/task/dto/task_dto.py +40 -40
  171. thestage/services/validation_service.py +61 -61
  172. {thestage-0.6.2.dist-info → thestage-0.6.4.dist-info}/LICENSE.txt +12 -12
  173. {thestage-0.6.2.dist-info → thestage-0.6.4.dist-info}/METADATA +3 -2
  174. thestage-0.6.4.dist-info/RECORD +176 -0
  175. {thestage-0.6.2.dist-info → thestage-0.6.4.dist-info}/WHEEL +1 -1
  176. thestage-0.6.2.dist-info/RECORD +0 -176
  177. {thestage-0.6.2.dist-info → thestage-0.6.4.dist-info}/entry_points.txt +0 -0
@@ -1,196 +1,193 @@
1
- from typing import Optional
2
- import typer
3
-
4
- from thestage.cli_command import CliCommand
5
- from thestage.cli_command_helper import check_command_permission
6
- from thestage.i18n.translation import __
7
- from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
8
- from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
9
- from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
10
- from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
11
- from thestage.services.abstract_service import AbstractService
12
- from thestage.helpers.error_handler import error_handler
13
- from thestage.services.clients.thestage_api.api_client import TheStageApiClient
14
- from thestage.services.clients.thestage_api.dtos.enums.task_status import TaskStatus
15
- from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
16
- from thestage.services.container.container_service import ContainerService
17
- from thestage.services.instance.instance_service import InstanceService
18
- from thestage.services.logging.logging_service import LoggingService
19
- from thestage.services.task.dto.task_dto import TaskDto
20
-
21
-
22
- class ConnectService(AbstractService):
23
- __thestage_api_client: TheStageApiClient = None
24
- __instance_service: InstanceService = None
25
- __container_service: ContainerService = None
26
- __logging_service: LoggingService = None
27
-
28
- def __init__(
29
- self,
30
- thestage_api_client: TheStageApiClient,
31
- instance_service: InstanceService,
32
- container_service: ContainerService,
33
- logging_service: LoggingService,
34
- ):
35
- super(ConnectService, self).__init__(
36
- )
37
- self.__thestage_api_client = thestage_api_client
38
- self.__instance_service = instance_service
39
- self.__container_service = container_service
40
- self.__logging_service = logging_service
41
-
42
-
43
- @error_handler()
44
- def connect_to_entity(
45
- self,
46
- uid: str,
47
- username: Optional[str],
48
- private_key_path: Optional[str],
49
- ):
50
- try:
51
- instance_selfhosted = self.__thestage_api_client.get_selfhosted_instance(instance_slug=uid)
52
- except HttpClientException as e:
53
- if e.get_status_code() == 403:
54
- typer.echo("Missing permission to view self-hosted instances")
55
- instance_selfhosted = None
56
-
57
- try:
58
- instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=uid)
59
- except HttpClientException as e:
60
- if e.get_status_code() == 403:
61
- typer.echo("Missing permission to view rented instances")
62
- instance_rented = None
63
-
64
- try:
65
- container = self.__thestage_api_client.get_container(container_slug=uid,)
66
- except HttpClientException as e:
67
- if e.get_status_code() == 403:
68
- typer.echo("Missing permission to view containers")
69
- container = None
70
-
71
- task: Optional[TaskDto] = None
72
- if uid.isdigit():
73
- try:
74
- task_view_response = self.__thestage_api_client.get_task(task_id=int(uid))
75
- except HttpClientException as e:
76
- if e.get_status_code() == 403:
77
- typer.echo("Missing permission to view tasks")
78
- task_view_response = None
79
- if task_view_response and task_view_response.task:
80
- task = task_view_response.task
81
-
82
- rented_exists = int(instance_rented is not None and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
83
- selfhosted_exists = int(instance_selfhosted is not None)
84
- container_exists = int(container is not None)
85
- task_exists = int(task is not None)
86
-
87
- rented_presence = int(rented_exists and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
88
- selfhosted_presence = int(selfhosted_exists and instance_selfhosted.frontend_status.status_key == SelfhostedBusinessStatus.RUNNING)
89
- container_presence = int(container_exists and (container.frontend_status.status_key == DockerContainerStatus.RUNNING or container.frontend_status.status_key == DockerContainerStatus.BUSY))
90
- task_presence = int(task_exists and task.frontend_status.status_key in [TaskStatus.RUNNING, TaskStatus.SCHEDULED])
91
-
92
- if rented_exists:
93
- typer.echo(__("Found a rented instance with the provided UID in status: '%rented_status%'", {"rented_status": instance_rented.frontend_status.status_translation}))
94
-
95
- if selfhosted_exists:
96
- typer.echo(__("Found a self-hosted instance with the provided UID in status: '%selfhosted_status%'", {"selfhosted_status": instance_selfhosted.frontend_status.status_translation}))
97
-
98
- if container_exists:
99
- typer.echo(__("Found a docker container with the provided UID in status: '%container_status%'", {"container_status": container.frontend_status.status_translation}))
100
-
101
- if task_exists:
102
- typer.echo(__("Found a task with the provided ID in status: '%task_status%'", {"task_status": task.frontend_status.status_translation}))
103
-
104
- if (rented_presence + selfhosted_presence + container_presence + task_presence) > 1:
105
- typer.echo("Provided identifier caused ambiguity")
106
- typer.echo("Consider running a dedicated command to connect to the entity you need")
107
- raise typer.Exit(code=1)
108
-
109
- if (rented_presence + selfhosted_presence + container_presence + task_presence) == 0:
110
- typer.echo("There is nothing to connect to with the provided identifier")
111
- raise typer.Exit(code=1)
112
-
113
- if rented_presence:
114
- check_command_permission(CliCommand.INSTANCE_RENTED_CONNECT)
115
- typer.echo("Connecting to rented instance...")
116
- self.__instance_service.connect_to_rented_instance(
117
- instance_rented_slug=uid,
118
- input_ssh_key_path=private_key_path
119
- )
120
-
121
- if container_presence:
122
- check_command_permission(CliCommand.CONTAINER_CONNECT)
123
- typer.echo("Connecting to docker container...")
124
- self.__container_service.connect_to_container(
125
- container_uid=uid,
126
- username=username,
127
- input_ssh_key_path=private_key_path
128
- )
129
-
130
- if selfhosted_presence:
131
- check_command_permission(CliCommand.INSTANCE_SELF_HOSTED_CONNECT)
132
- typer.echo("Connecting to self-hosted instance...")
133
-
134
- self.__instance_service.connect_to_selfhosted_instance(
135
- selfhosted_instance_slug=uid,
136
- username=username,
137
- input_ssh_key_path=private_key_path
138
- )
139
-
140
- if task_presence:
141
- typer.echo(__("Connecting to task..."))
142
- self.__logging_service.stream_task_logs_with_controls(task_id=int(uid))
143
-
144
-
145
- @error_handler()
146
- def upload_ssh_key(self, public_key_contents: str, instance_slug: Optional[str]):
147
- instance_rented: Optional[InstanceRentedDto] = None
148
- if instance_slug:
149
- try:
150
- instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=instance_slug)
151
- except HttpClientException as e:
152
- instance_rented = None
153
-
154
- # if no instances found - exit 1
155
- if instance_rented is None:
156
- typer.echo(f"No rented instance found with matching unique ID '{instance_slug}'")
157
- raise typer.Exit(1)
158
-
159
- note_to_send: Optional[str] = None
160
-
161
- is_user_already_has_key_response = self.__thestage_api_client.is_user_has_ssh_public_key(
162
- public_key=public_key_contents
163
- )
164
-
165
- ssh_key_pair_id = is_user_already_has_key_response.sshKeyPairId
166
- is_adding_key_to_user = not is_user_already_has_key_response.isUserHasPublicKey
167
-
168
- if is_adding_key_to_user and not note_to_send:
169
- note_to_send: str = typer.prompt(
170
- text=__('SSH key will be added to your profile. Please provide a title for this key'),
171
- show_choices=False,
172
- type=str,
173
- show_default=False,
174
- )
175
-
176
- if not is_adding_key_to_user and not instance_rented:
177
- typer.echo("Key already exists on your profile")
178
-
179
- if is_adding_key_to_user:
180
- add_ssh_key_to_user_response = self.__thestage_api_client.add_public_ssh_key_to_user(
181
- public_key=public_key_contents,
182
- note=note_to_send
183
- )
184
- typer.echo(f"Public key '{note_to_send}' added to your profile")
185
- ssh_key_pair_id = add_ssh_key_to_user_response.sshKeyPairId
186
-
187
- if instance_rented:
188
- self.__thestage_api_client.add_public_ssh_key_to_instance_rented(
189
- instance_rented_id=instance_rented.id,
190
- ssh_key_pair_id=ssh_key_pair_id
191
- )
192
-
193
- if instance_rented.frontend_status.status_key != InstanceRentedBusinessStatus.ONLINE:
194
- typer.echo(f"Rented instance '{instance_rented.slug}' status is '{instance_rented.frontend_status.status_translation}'. Key will be added as soon as it is back online.")
195
- else:
196
- typer.echo(f"Public key added to rented instance '{instance_rented.slug}'")
1
+ from typing import Optional
2
+ import typer
3
+
4
+ from thestage.cli_command import CliCommand
5
+ from thestage.cli_command_helper import check_command_permission
6
+ from thestage.i18n.translation import __
7
+ from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
8
+ from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
9
+ from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
10
+ from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
11
+ from thestage.services.abstract_service import AbstractService
12
+ from thestage.helpers.error_handler import error_handler
13
+ from thestage.services.clients.thestage_api.api_client import TheStageApiClient
14
+ from thestage.services.clients.thestage_api.dtos.enums.task_status import TaskStatus
15
+ from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
16
+ from thestage.services.container.container_service import ContainerService
17
+ from thestage.services.instance.instance_service import InstanceService
18
+ from thestage.services.logging.logging_service import LoggingService
19
+ from thestage.services.task.dto.task_dto import TaskDto
20
+ from thestage.helpers.logger.app_logger import app_logger
21
+
22
+
23
+ class ConnectService(AbstractService):
24
+ __thestage_api_client: TheStageApiClient = None
25
+ __instance_service: InstanceService = None
26
+ __container_service: ContainerService = None
27
+ __logging_service: LoggingService = None
28
+
29
+ def __init__(
30
+ self,
31
+ thestage_api_client: TheStageApiClient,
32
+ instance_service: InstanceService,
33
+ container_service: ContainerService,
34
+ logging_service: LoggingService,
35
+ ):
36
+ super(ConnectService, self).__init__(
37
+ )
38
+ self.__thestage_api_client = thestage_api_client
39
+ self.__instance_service = instance_service
40
+ self.__container_service = container_service
41
+ self.__logging_service = logging_service
42
+
43
+
44
+ @error_handler()
45
+ def connect_to_entity(
46
+ self,
47
+ uid: str,
48
+ username: Optional[str],
49
+ private_key_path: Optional[str],
50
+ ):
51
+ try:
52
+ instance_selfhosted = self.__thestage_api_client.get_selfhosted_instance(instance_slug=uid)
53
+ except HttpClientException as e:
54
+ app_logger.warn(f"get_selfhosted_instance: code {e.get_status_code()}")
55
+ instance_selfhosted = None
56
+
57
+ try:
58
+ instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=uid)
59
+ except HttpClientException as e:
60
+ app_logger.warn(f"get_rented_instance: code {e.get_status_code()}")
61
+ instance_rented = None
62
+
63
+ try:
64
+ container = self.__thestage_api_client.get_container(container_slug=uid,)
65
+ except HttpClientException as e:
66
+ app_logger.warn(f"get_container: code {e.get_status_code()}")
67
+ container = None
68
+
69
+ task: Optional[TaskDto] = None
70
+ if uid.isdigit():
71
+ try:
72
+ task_view_response = self.__thestage_api_client.get_task(task_id=int(uid))
73
+ except HttpClientException as e:
74
+ app_logger.warn(f"get_task error: code {e.get_status_code()}")
75
+ task_view_response = None
76
+ if task_view_response and task_view_response.task:
77
+ task = task_view_response.task
78
+
79
+ rented_exists = int(instance_rented is not None and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
80
+ selfhosted_exists = int(instance_selfhosted is not None)
81
+ container_exists = int(container is not None)
82
+ task_exists = int(task is not None)
83
+
84
+ rented_presence = int(rented_exists and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
85
+ selfhosted_presence = int(selfhosted_exists and instance_selfhosted.frontend_status.status_key == SelfhostedBusinessStatus.RUNNING)
86
+ container_presence = int(container_exists and (container.frontend_status.status_key == DockerContainerStatus.RUNNING or container.frontend_status.status_key == DockerContainerStatus.BUSY))
87
+ task_presence = int(task_exists and task.frontend_status.status_key in [TaskStatus.RUNNING, TaskStatus.SCHEDULED])
88
+
89
+ if rented_exists:
90
+ typer.echo(__("Found a rented instance with the provided UID in status: '%rented_status%'", {"rented_status": instance_rented.frontend_status.status_translation}))
91
+
92
+ if selfhosted_exists:
93
+ typer.echo(__("Found a self-hosted instance with the provided UID in status: '%selfhosted_status%'", {"selfhosted_status": instance_selfhosted.frontend_status.status_translation}))
94
+
95
+ if container_exists:
96
+ typer.echo(__("Found a docker container with the provided UID in status: '%container_status%'", {"container_status": container.frontend_status.status_translation}))
97
+
98
+ if task_exists:
99
+ typer.echo(__("Found a task with the provided ID in status: '%task_status%'", {"task_status": task.frontend_status.status_translation}))
100
+
101
+ if (rented_presence + selfhosted_presence + container_presence + task_presence) > 1:
102
+ typer.echo("Provided identifier caused ambiguity")
103
+ typer.echo("Consider running a dedicated command to connect to the entity you need")
104
+ raise typer.Exit(code=1)
105
+
106
+ if (rented_presence + selfhosted_presence + container_presence + task_presence) == 0:
107
+ typer.echo("There is nothing to connect to with the provided identifier")
108
+ raise typer.Exit(code=1)
109
+
110
+ if rented_presence:
111
+ check_command_permission(CliCommand.INSTANCE_RENTED_CONNECT)
112
+ typer.echo("Connecting to rented instance...")
113
+ self.__instance_service.connect_to_rented_instance(
114
+ instance_rented_slug=uid,
115
+ input_ssh_key_path=private_key_path
116
+ )
117
+
118
+ if container_presence:
119
+ check_command_permission(CliCommand.CONTAINER_CONNECT)
120
+ typer.echo("Connecting to docker container...")
121
+ self.__container_service.connect_to_container(
122
+ container_uid=uid,
123
+ username=username,
124
+ input_ssh_key_path=private_key_path
125
+ )
126
+
127
+ if selfhosted_presence:
128
+ check_command_permission(CliCommand.INSTANCE_SELF_HOSTED_CONNECT)
129
+ typer.echo("Connecting to self-hosted instance...")
130
+
131
+ self.__instance_service.connect_to_selfhosted_instance(
132
+ selfhosted_instance_slug=uid,
133
+ username=username,
134
+ input_ssh_key_path=private_key_path
135
+ )
136
+
137
+ if task_presence:
138
+ typer.echo(__("Connecting to task..."))
139
+ self.__logging_service.stream_task_logs_with_controls(task_id=int(uid))
140
+
141
+
142
+ @error_handler()
143
+ def upload_ssh_key(self, public_key_contents: str, instance_slug: Optional[str]):
144
+ instance_rented: Optional[InstanceRentedDto] = None
145
+ if instance_slug:
146
+ try:
147
+ instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=instance_slug)
148
+ except HttpClientException as e:
149
+ instance_rented = None
150
+
151
+ # if no instances found - exit 1
152
+ if instance_rented is None:
153
+ typer.echo(f"No rented instance found with matching unique ID '{instance_slug}'")
154
+ raise typer.Exit(1)
155
+
156
+ note_to_send: Optional[str] = None
157
+
158
+ is_user_already_has_key_response = self.__thestage_api_client.is_user_has_ssh_public_key(
159
+ public_key=public_key_contents
160
+ )
161
+
162
+ ssh_key_pair_id = is_user_already_has_key_response.sshKeyPairId
163
+ is_adding_key_to_user = not is_user_already_has_key_response.isUserHasPublicKey
164
+
165
+ if is_adding_key_to_user and not note_to_send:
166
+ note_to_send: str = typer.prompt(
167
+ text=__('SSH key will be added to your profile. Please provide a title for this key'),
168
+ show_choices=False,
169
+ type=str,
170
+ show_default=False,
171
+ )
172
+
173
+ if not is_adding_key_to_user and not instance_rented:
174
+ typer.echo("Key already exists on your profile")
175
+
176
+ if is_adding_key_to_user:
177
+ add_ssh_key_to_user_response = self.__thestage_api_client.add_public_ssh_key_to_user(
178
+ public_key=public_key_contents,
179
+ note=note_to_send
180
+ )
181
+ typer.echo(f"Public key '{note_to_send}' added to your profile")
182
+ ssh_key_pair_id = add_ssh_key_to_user_response.sshKeyPairId
183
+
184
+ if instance_rented:
185
+ self.__thestage_api_client.add_public_ssh_key_to_instance_rented(
186
+ instance_rented_id=instance_rented.id,
187
+ ssh_key_pair_id=ssh_key_pair_id
188
+ )
189
+
190
+ if instance_rented.frontend_status.status_key != InstanceRentedBusinessStatus.ONLINE:
191
+ typer.echo(f"Rented instance '{instance_rented.slug}' status is '{instance_rented.frontend_status.status_translation}'. Key will be added as soon as it is back online.")
192
+ else:
193
+ typer.echo(f"Public key added to rented instance '{instance_rented.slug}'")
@@ -1,9 +1,9 @@
1
- from typing import Optional, Dict
2
-
3
- from pydantic import Field, ConfigDict, BaseModel
4
-
5
-
6
- class RemoteServerConfig(BaseModel):
7
- model_config = ConfigDict(use_enum_values=True)
8
-
9
- ip_address_to_ssh_key_map: Dict[str, str] = Field(None, alias='ip_address_to_ssh_key_map')
1
+ from typing import Optional, Dict
2
+
3
+ from pydantic import Field, ConfigDict, BaseModel
4
+
5
+
6
+ class RemoteServerConfig(BaseModel):
7
+ model_config = ConfigDict(use_enum_values=True)
8
+
9
+ ip_address_to_ssh_key_map: Dict[str, str] = Field(None, alias='ip_address_to_ssh_key_map')
File without changes