thestage 0.6.7__py3-none-any.whl → 0.7.0__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 (202) hide show
  1. thestage/__init__.py +1 -1
  2. thestage/cli_command.py +3 -0
  3. thestage/cli_command_helper.py +2 -2
  4. thestage/config/__init__.py +1 -1
  5. thestage/{services → config/business}/app_config_service.py +3 -4
  6. thestage/{services/config_provider → config/business}/config_provider.py +6 -5
  7. thestage/config/{config_storage.py → business/config_storage.py} +1 -1
  8. thestage/{services → config/business}/validation_service.py +9 -10
  9. thestage/{controllers/config_controller.py → config/communication/config_command.py} +7 -7
  10. thestage/{services/connect → connect/business}/connect_service.py +22 -19
  11. thestage/{services → connect/business}/remote_server_service.py +4 -5
  12. thestage/connect/communication/connect_api_client.py +84 -0
  13. thestage/{services/clients/thestage_api/dtos/ssh_key_controller → connect/dto}/add_ssh_key_to_user_response.py +0 -1
  14. thestage/{services/clients/thestage_api/dtos/ssh_key_controller → connect/dto}/add_ssh_public_key_to_instance_response.py +1 -4
  15. thestage/{services/clients/thestage_api/dtos/base_controller → connect/dto}/connect_resolve_response.py +0 -1
  16. thestage/controllers/base_controller.py +2 -2
  17. thestage/debug_main.dist.py +16 -14
  18. thestage/{services/container → docker_container/business}/container_service.py +147 -22
  19. thestage/{services/container → docker_container/business}/mapper/container_mapper.py +3 -3
  20. thestage/docker_container/business/mapper/image_mapper.py +16 -0
  21. thestage/docker_container/communication/__init__.py +0 -0
  22. thestage/{controllers/container_controller.py → docker_container/communication/docker_command.py} +192 -29
  23. thestage/docker_container/communication/docker_container_api_client.py +139 -0
  24. thestage/docker_container/communication/image_command.py +40 -0
  25. thestage/docker_container/dto/__init__.py +0 -0
  26. thestage/docker_container/dto/container_action_request.py +11 -0
  27. thestage/{services/clients/thestage_api/dtos → docker_container/dto}/container_response.py +11 -4
  28. thestage/docker_container/dto/docker_container_create_request.py +28 -0
  29. thestage/docker_container/dto/docker_container_create_response.py +11 -0
  30. thestage/{services/clients/thestage_api/dtos/docker_container_controller → docker_container/dto}/docker_container_list_response.py +3 -5
  31. thestage/docker_container/dto/docker_image_list_request.py +11 -0
  32. thestage/docker_container/dto/docker_image_list_response.py +13 -0
  33. thestage/docker_container/dto/enum/__init__.py +0 -0
  34. thestage/docker_container/dto/image_entity.py +14 -0
  35. thestage/git/__init__.py +0 -0
  36. thestage/git/business/__init__.py +0 -0
  37. thestage/git/communication/__init__.py +0 -0
  38. thestage/{services/clients/git → git/communication}/git_client.py +4 -4
  39. thestage/global_dto/__init__.py +0 -0
  40. thestage/global_dto/enums/__init__.py +0 -0
  41. thestage/helpers/error_handler.py +4 -4
  42. thestage/helpers/logger/app_logger.py +1 -3
  43. thestage/i18n/en_GB/messages.po +14 -14
  44. thestage/inference_model/__init__.py +0 -0
  45. thestage/inference_model/business/__init__.py +0 -0
  46. thestage/inference_model/business/inference_model_service.py +281 -0
  47. thestage/inference_model/business/mapper/__init__.py +0 -0
  48. thestage/{services/project/mapper/project_inference_simulator_model_mapper.py → inference_model/business/mapper/inference_model_mapper.py} +5 -5
  49. thestage/inference_model/communication/__init__.py +0 -0
  50. thestage/inference_model/communication/inference_model_api_client.py +139 -0
  51. thestage/inference_model/communication/inference_model_command.py +246 -0
  52. thestage/inference_model/dto/__init__.py +0 -0
  53. thestage/{services/clients/thestage_api/dtos/inference_controller → inference_model/dto}/deploy_inference_model_to_instance_response.py +0 -1
  54. thestage/inference_model/dto/enum/__init__.py +0 -0
  55. thestage/{services/project/dto/inference_simulator_model_dto.py → inference_model/dto/inference_model.py} +1 -1
  56. thestage/{entities/project_inference_simulator_model.py → inference_model/dto/inference_model_entity.py} +1 -1
  57. thestage/{services/clients/thestage_api/dtos/inference_controller → inference_model/dto}/inference_simulator_model_list_for_project_response.py +2 -3
  58. thestage/{services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py → inference_model/dto/push_inference_simulator_model_request.py} +1 -1
  59. thestage/{services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_response.py → inference_model/dto/push_inference_simulator_model_response.py} +1 -1
  60. thestage/inference_simulator/__init__.py +0 -0
  61. thestage/inference_simulator/business/__init__.py +0 -0
  62. thestage/inference_simulator/business/inference_simulator_service.py +338 -0
  63. thestage/inference_simulator/business/mapper/__init__.py +0 -0
  64. thestage/{services/project/mapper/project_inference_simulator_mapper.py → inference_simulator/business/mapper/inference_simulator_mapper.py} +5 -5
  65. thestage/inference_simulator/communication/__init__.py +0 -0
  66. thestage/inference_simulator/communication/inference_simulator_api_client.py +114 -0
  67. thestage/inference_simulator/communication/inference_simulator_command.py +347 -0
  68. thestage/inference_simulator/dto/__init__.py +0 -0
  69. thestage/inference_simulator/dto/enum/__init__.py +0 -0
  70. thestage/inference_simulator/dto/get_inference_simulator_response.py +12 -0
  71. thestage/{services/project/dto/inference_simulator_dto.py → inference_simulator/dto/inference_simulator.py} +1 -1
  72. thestage/{entities/project_inference_simulator.py → inference_simulator/dto/inference_simulator_entity.py} +1 -1
  73. thestage/{services/clients/thestage_api/dtos/inference_controller → inference_simulator/dto}/inference_simulator_list_response.py +2 -2
  74. thestage/{services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py → inference_simulator/dto/start_inference_simulator_request.py} +1 -1
  75. thestage/inference_simulator/dto/start_inference_simulator_response.py +10 -0
  76. thestage/instance/__init__.py +0 -0
  77. thestage/instance/business/__init__.py +0 -0
  78. thestage/{services/instance → instance/business}/instance_service.py +26 -27
  79. thestage/instance/business/mapper/__init__.py +0 -0
  80. thestage/{services/instance/mapper/instance_mapper.py → instance/business/mapper/rented_instance_mapper.py} +3 -3
  81. thestage/{services/instance/mapper/selfhosted_mapper.py → instance/business/mapper/selfhosted_instance_mapper.py} +5 -7
  82. thestage/instance/communication/__init__.py +0 -0
  83. thestage/instance/communication/instance_api_client.py +150 -0
  84. thestage/{controllers/instance_controller.py → instance/communication/instance_command.py} +5 -5
  85. thestage/instance/dto/__init__.py +0 -0
  86. thestage/instance/dto/enum/__init__.py +0 -0
  87. thestage/{services/clients/thestage_api/dtos → instance/dto}/instance_detected_gpus.py +1 -2
  88. thestage/{services/clients/thestage_api/dtos → instance/dto}/instance_rented_response.py +2 -2
  89. thestage/{services/clients/thestage_api/dtos → instance/dto}/selfhosted_instance_response.py +2 -3
  90. thestage/logging/__init__.py +0 -0
  91. thestage/logging/business/__init__.py +0 -0
  92. thestage/{services/logging → logging/business}/logging_service.py +40 -28
  93. thestage/logging/communication/__init__.py +0 -0
  94. thestage/logging/communication/logging_api_client.py +63 -0
  95. thestage/logging/dto/__init__.py +0 -0
  96. thestage/{services/clients/thestage_api/dtos/logging_controller → logging/dto}/log_polling_response.py +2 -2
  97. thestage/{services/clients/thestage_api/dtos/logging_controller → logging/dto}/user_logs_query_response.py +2 -2
  98. thestage/main.py +54 -8
  99. thestage/project/__init__.py +0 -0
  100. thestage/project/business/__init__.py +0 -0
  101. thestage/project/business/project_service.py +480 -0
  102. thestage/project/communication/__init__.py +0 -0
  103. thestage/project/communication/project_api_client.py +46 -0
  104. thestage/project/communication/project_command.py +284 -0
  105. thestage/project/dto/__init__.py +0 -0
  106. thestage/services/clients/thestage_api/core/api_client_core.py +1 -1
  107. thestage/services/clients/thestage_api/dtos/entity_filter_request.py +1 -1
  108. thestage/services/clients/thestage_api/dtos/sftp_path_helper.py +1 -1
  109. thestage/services/filesystem_service.py +2 -2
  110. thestage/services/service_factory.py +130 -43
  111. thestage/task/__init__.py +0 -0
  112. thestage/task/business/__init__.py +0 -0
  113. thestage/task/business/mapper/__init__.py +0 -0
  114. thestage/{services/project/mapper/project_task_mapper.py → task/business/mapper/task_mapper.py} +5 -5
  115. thestage/task/business/task_service.py +307 -0
  116. thestage/task/communication/__init__.py +0 -0
  117. thestage/task/communication/task_api_client.py +122 -0
  118. thestage/task/communication/task_command.py +212 -0
  119. thestage/task/dto/__init__.py +0 -0
  120. thestage/task/dto/enum/__init__.py +0 -0
  121. thestage/{services/clients/thestage_api/dtos/task_controller/task_list_for_project_response.py → task/dto/list_for_project_response.py} +2 -2
  122. thestage/{services/clients/thestage_api/dtos/project_controller/project_run_task_request.py → task/dto/run_task_request.py} +1 -1
  123. thestage/task/dto/run_task_response.py +13 -0
  124. thestage/{services/task/dto/task_dto.py → task/dto/task.py} +1 -4
  125. thestage/{entities/project_task.py → task/dto/task_entity.py} +1 -1
  126. thestage/{services/clients/thestage_api/dtos/task_controller/task_view_response.py → task/dto/view_response.py} +2 -2
  127. {thestage-0.6.7.dist-info → thestage-0.7.0.dist-info}/METADATA +1 -1
  128. thestage-0.7.0.dist-info/RECORD +226 -0
  129. {thestage-0.6.7.dist-info → thestage-0.7.0.dist-info}/WHEEL +1 -1
  130. thestage/controllers/project_controller.py +0 -1056
  131. thestage/services/clients/thestage_api/api_client.py +0 -751
  132. thestage/services/clients/thestage_api/dtos/container_param_request.py +0 -11
  133. thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_response.py +0 -13
  134. thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_response.py +0 -13
  135. thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_response.py +0 -10
  136. thestage/services/clients/thestage_api/dtos/user_controller/user_profile.py +0 -12
  137. thestage/services/project/project_service.py +0 -1287
  138. thestage-0.6.7.dist-info/RECORD +0 -167
  139. /thestage/{entities → color_scheme}/__init__.py +0 -0
  140. /thestage/{entities/enums → config/business}/__init__.py +0 -0
  141. /thestage/{services/clients/git → config/communication}/__init__.py +0 -0
  142. /thestage/{services/clients/thestage_api/dtos/enums → config/dto}/__init__.py +0 -0
  143. /thestage/{services/core_files → config/dto}/config_entity.py +0 -0
  144. /thestage/{services/connect → config}/dto/remote_server_config.py +0 -0
  145. /thestage/{services/config_provider → connect}/__init__.py +0 -0
  146. /thestage/{services/container → connect/business}/__init__.py +0 -0
  147. /thestage/{services/container/mapper → connect/communication}/__init__.py +0 -0
  148. /thestage/{services/instance → connect/dto}/__init__.py +0 -0
  149. /thestage/{services/clients/thestage_api/dtos/ssh_key_controller → connect/dto}/add_ssh_key_to_user_request.py +0 -0
  150. /thestage/{services/clients/thestage_api/dtos/ssh_key_controller → connect/dto}/add_ssh_public_key_to_instance_request.py +0 -0
  151. /thestage/{services/clients/thestage_api/dtos/ssh_key_controller → connect/dto}/is_user_has_public_ssh_key_request.py +0 -0
  152. /thestage/{services/clients/thestage_api/dtos/ssh_key_controller → connect/dto}/is_user_has_public_ssh_key_response.py +0 -0
  153. /thestage/{services/instance/mapper → docker_container}/__init__.py +0 -0
  154. /thestage/{services/project → docker_container/business}/__init__.py +0 -0
  155. /thestage/{services/project → docker_container/business}/mapper/__init__.py +0 -0
  156. /thestage/{entities/container.py → docker_container/dto/container_entity.py} +0 -0
  157. /thestage/{services/clients/thestage_api/dtos/docker_container_controller → docker_container/dto}/docker_container_list_request.py +0 -0
  158. /thestage/{services/clients/thestage_api/dtos → docker_container/dto}/docker_container_mapping.py +0 -0
  159. /thestage/{services/clients/thestage_api/dtos/enums → docker_container/dto/enum}/container_pending_action.py +0 -0
  160. /thestage/{services/clients/thestage_api/dtos/enums → docker_container/dto/enum}/container_status.py +0 -0
  161. /thestage/{services/logging/exception → exceptions}/log_polling_exception.py +0 -0
  162. /thestage/git/{ProgressPrinter.py → business/ProgressPrinter.py} +0 -0
  163. /thestage/{entities → global_dto}/enums/order_direction_type.py +0 -0
  164. /thestage/{entities → global_dto}/enums/shell_type.py +0 -0
  165. /thestage/{entities → global_dto}/enums/tail_output_type.py +0 -0
  166. /thestage/{entities → global_dto}/enums/yes_no_response.py +0 -0
  167. /thestage/{entities → global_dto}/file_item.py +0 -0
  168. /thestage/{services/clients/thestage_api/dtos/inference_controller → inference_model/dto}/deploy_inference_model_to_instance_request.py +0 -0
  169. /thestage/{services/clients/thestage_api/dtos/inference_controller → inference_model/dto}/deploy_inference_model_to_sagemaker_request.py +0 -0
  170. /thestage/{services/clients/thestage_api/dtos/inference_controller → inference_model/dto}/deploy_inference_model_to_sagemaker_response.py +0 -0
  171. /thestage/{services/clients/thestage_api/dtos/enums → inference_model/dto/enum}/inference_model_status.py +0 -0
  172. /thestage/{services/clients/thestage_api/dtos/inference_controller → inference_model/dto}/inference_simulator_model_list_for_project_request.py +0 -0
  173. /thestage/{services/clients/thestage_api/dtos → inference_model/dto}/inference_simulator_model_response.py +0 -0
  174. /thestage/{services/clients/thestage_api/dtos/enums → inference_simulator/dto/enum}/inference_simulator_status.py +0 -0
  175. /thestage/{services/clients/thestage_api/dtos/inference_controller → inference_simulator/dto}/get_inference_simulator_request.py +0 -0
  176. /thestage/{services/clients/thestage_api/dtos/inference_controller → inference_simulator/dto}/inference_simulator_list_request.py +0 -0
  177. /thestage/{services/clients/thestage_api/dtos → inference_simulator/dto}/inference_simulator_response.py +0 -0
  178. /thestage/{services/clients/thestage_api/dtos/enums → instance/dto/enum}/cpu_type.py +0 -0
  179. /thestage/{services/clients/thestage_api/dtos/enums → instance/dto/enum}/gpu_name.py +0 -0
  180. /thestage/{services/clients/thestage_api/dtos/enums → instance/dto/enum}/instance_rented_status.py +0 -0
  181. /thestage/{services/clients/thestage_api/dtos/enums → instance/dto/enum}/provider_name.py +0 -0
  182. /thestage/{services/clients/thestage_api/dtos/enums → instance/dto/enum}/selfhosted_status.py +0 -0
  183. /thestage/{entities → instance/dto}/rented_instance.py +0 -0
  184. /thestage/{entities → instance/dto}/self_hosted_instance.py +0 -0
  185. /thestage/{services/logging → logging}/byte_print_style.py +0 -0
  186. /thestage/{services/clients/thestage_api/dtos/logging_controller → logging/dto}/docker_container_log_stream_request.py +0 -0
  187. /thestage/{services/logging → logging}/dto/log_message.py +0 -0
  188. /thestage/{services/clients/thestage_api/dtos/logging_controller → logging/dto}/log_polling_request.py +0 -0
  189. /thestage/{services/logging → logging}/dto/log_type.py +0 -0
  190. /thestage/{services/clients/thestage_api/dtos/logging_controller → logging/dto}/task_log_stream_request.py +0 -0
  191. /thestage/{services/clients/thestage_api/dtos/logging_controller → logging/dto}/user_logs_query_request.py +0 -0
  192. /thestage/{services/logging → logging}/logging_constants.py +0 -0
  193. /thestage/{services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py → project/dto/get_deploy_ssh_key_request.py} +0 -0
  194. /thestage/{services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_response.py → project/dto/get_deploy_ssh_key_response.py} +0 -0
  195. /thestage/{services/project → project}/dto/project_config.py +0 -0
  196. /thestage/{services/clients/thestage_api/dtos → project/dto}/project_response.py +0 -0
  197. /thestage/{services/clients/thestage_api/dtos/enums → task/dto/enum}/task_execution_status.py +0 -0
  198. /thestage/{services/clients/thestage_api/dtos/enums → task/dto/enum}/task_status.py +0 -0
  199. /thestage/{services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py → task/dto/list_for_project_request.py} +0 -0
  200. /thestage/{services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py → task/dto/status_localized_map_response.py} +0 -0
  201. {thestage-0.6.7.dist-info → thestage-0.7.0.dist-info}/entry_points.txt +0 -0
  202. {thestage-0.6.7.dist-info → thestage-0.7.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -6,37 +6,47 @@ from rich import print
6
6
  import typer
7
7
 
8
8
  from thestage.color_scheme.color_scheme import ColorScheme
9
- from thestage.entities.container import DockerContainerEntity
10
- from thestage.services.clients.thestage_api.dtos.container_param_request import DockerContainerActionRequestDto
11
- from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
12
- from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
13
- from thestage.entities.enums.shell_type import ShellType
9
+ from thestage.connect.communication.connect_api_client import ConnectApiClient
10
+ from thestage.docker_container.business.mapper.image_mapper import ImageMapper
11
+ from thestage.docker_container.communication.docker_container_api_client import DockerContainerApiClient
12
+ from thestage.docker_container.dto.container_entity import DockerContainerEntity
13
+ from thestage.docker_container.dto.container_action_request import DockerContainerActionRequest
14
+ from thestage.docker_container.dto.docker_container_create_request import DockerContainerCreateRequest, \
15
+ DockerContainerMappings
16
+ from thestage.docker_container.dto.enum.container_pending_action import DockerContainerAction
17
+ from thestage.docker_container.dto.enum.container_status import DockerContainerStatus
18
+ from thestage.docker_container.dto.image_entity import DockerImageEntity
19
+ from thestage.global_dto.enums.shell_type import ShellType
20
+ from thestage.project.dto.project_config import ProjectConfig
14
21
  from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
15
- from thestage.services.container.mapper.container_mapper import ContainerMapper
22
+ from thestage.docker_container.business.mapper.container_mapper import ContainerMapper
16
23
  from thestage.services.filesystem_service import FileSystemService
17
- from thestage.services.remote_server_service import RemoteServerService
24
+ from thestage.connect.business.remote_server_service import RemoteServerService
18
25
  from thestage.i18n.translation import __
19
26
  from thestage.services.abstract_service import AbstractService
20
- from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
27
+ from thestage.docker_container.dto.container_response import DockerContainerDto, DockerImageDto
21
28
  from thestage.helpers.error_handler import error_handler
22
- from thestage.services.clients.thestage_api.api_client import TheStageApiClient
23
- from thestage.services.config_provider.config_provider import ConfigProvider
29
+ from thestage.config.business.config_provider import ConfigProvider
24
30
 
25
31
 
26
32
  class ContainerService(AbstractService):
27
33
 
28
- __thestage_api_client: TheStageApiClient = None
34
+ __docker_container_api_client: DockerContainerApiClient = None
29
35
  __config_provider: ConfigProvider = None
36
+ __connect_api_client: ConnectApiClient = None
30
37
 
31
38
  def __init__(
32
39
  self,
33
- thestage_api_client: TheStageApiClient,
40
+ docker_container_api_client: DockerContainerApiClient,
34
41
  config_provider: ConfigProvider,
35
42
  remote_server_service: RemoteServerService,
36
43
  file_system_service: FileSystemService,
44
+ connect_api_client: ConnectApiClient,
45
+
37
46
  ):
38
47
  self.__config_provider = config_provider
39
- self.__thestage_api_client = thestage_api_client
48
+ self.__connect_api_client = connect_api_client
49
+ self.__docker_container_api_client = docker_container_api_client
40
50
  self.__remote_server_service = remote_server_service
41
51
  self.__file_system_service = file_system_service
42
52
 
@@ -50,7 +60,7 @@ class ContainerService(AbstractService):
50
60
  project_slug: Optional[str],
51
61
  statuses: List[str],
52
62
  ):
53
- container_status_map = self.__thestage_api_client.get_container_business_status_map()
63
+ container_status_map = self.__docker_container_api_client.get_container_business_status_map()
54
64
 
55
65
  if not statuses:
56
66
  statuses = ({key: container_status_map[key] for key in [
@@ -103,7 +113,7 @@ class ContainerService(AbstractService):
103
113
  project_slug: Optional[str] = None,
104
114
  ) -> PaginatedEntityList[DockerContainerDto]:
105
115
 
106
- list = self.__thestage_api_client.get_container_list(
116
+ list = self.__docker_container_api_client.get_container_list(
107
117
  statuses=statuses,
108
118
  page=page,
109
119
  limit=row,
@@ -113,6 +123,7 @@ class ContainerService(AbstractService):
113
123
 
114
124
  return list
115
125
 
126
+
116
127
  # TODO delete this proxy method
117
128
  @error_handler()
118
129
  def get_container(
@@ -120,7 +131,7 @@ class ContainerService(AbstractService):
120
131
  container_public_id: Optional[str] = None,
121
132
  container_slug: Optional[str] = None,
122
133
  ) -> Optional[DockerContainerDto]:
123
- return self.__thestage_api_client.get_container(
134
+ return self.__docker_container_api_client.get_container(
124
135
  container_public_id=container_public_id,
125
136
  container_slug=container_slug,
126
137
  )
@@ -287,7 +298,7 @@ class ContainerService(AbstractService):
287
298
  typer.echo('Container identifier (container_id_or_name) is required')
288
299
  raise typer.Exit(1)
289
300
 
290
- resolved_options = self.__thestage_api_client.resolve_user_input(entity_identifier=container_identifier)
301
+ resolved_options = self.__connect_api_client.resolve_user_input(entity_identifier=container_identifier)
291
302
  container_public_id = None
292
303
  valid_container_count = 0
293
304
  for container_item in resolved_options.dockerContainerMatchData:
@@ -302,7 +313,7 @@ class ContainerService(AbstractService):
302
313
  typer.echo(f"Failed to resolve the container by provided identifier, as total of {valid_container_count} containers are valid options")
303
314
  raise typer.Exit(1)
304
315
 
305
- container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
316
+ container: Optional[DockerContainerDto] = self.__docker_container_api_client.get_container(
306
317
  container_public_id=container_public_id,
307
318
  )
308
319
 
@@ -366,7 +377,7 @@ class ContainerService(AbstractService):
366
377
  typer.echo('Container identifier (container_id_or_name) is required')
367
378
  raise typer.Exit(1)
368
379
 
369
- resolved_options = self.__thestage_api_client.resolve_user_input(entity_identifier=container_identifier)
380
+ resolved_options = self.__connect_api_client.resolve_user_input(entity_identifier=container_identifier)
370
381
  container_public_id = None
371
382
  valid_container_count = 0
372
383
  for container_item in resolved_options.dockerContainerMatchData:
@@ -381,7 +392,7 @@ class ContainerService(AbstractService):
381
392
  typer.echo(f"Failed to resolve the container by provided identifier, as total of {valid_container_count} containers are valid options")
382
393
  raise typer.Exit(1)
383
394
 
384
- container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
395
+ container: Optional[DockerContainerDto] = self.__docker_container_api_client.get_container(
385
396
  container_public_id=container_public_id,
386
397
  )
387
398
 
@@ -441,13 +452,127 @@ class ContainerService(AbstractService):
441
452
  if action in [DockerContainerAction.STOP, DockerContainerAction.RESTART]:
442
453
  self.check_if_container_running(container=container)
443
454
 
444
- request_params = DockerContainerActionRequestDto(
455
+ request_params = DockerContainerActionRequest(
445
456
  dockerContainerPublicId=container.public_id,
446
457
  action=action,
447
458
  )
448
- result = self.__thestage_api_client.container_action(
459
+ result = self.__docker_container_api_client.container_action(
449
460
  request_param=request_params,
450
461
  )
451
462
 
452
463
  if result.is_success:
453
464
  typer.echo(f'Docker container action scheduled: {action.value}')
465
+
466
+ @error_handler()
467
+ def create_docker_container(
468
+ self,
469
+ project_public_id: Optional[str],
470
+ project_slug: Optional[str],
471
+ container_slug: str,
472
+ rented_instance_public_id: Optional[str],
473
+ rented_instance_slug: Optional[str],
474
+ self_hosted_instance_public_id: Optional[str],
475
+ self_hosted_instance_slug: Optional[str],
476
+ image_name: str,
477
+ cpu_number: Optional[int],
478
+ gpu_number: Optional[int],
479
+ volume_mappings: Optional[Dict[str, str]],
480
+ ):
481
+ if not project_public_id and not project_slug:
482
+ project_config: ProjectConfig = self.__config_provider.read_project_config()
483
+ if not project_config:
484
+ typer.echo("Provide the project unique ID or name, or run this command from within an initialized project directory")
485
+ raise typer.Exit(1)
486
+ project_public_id = project_config.public_id
487
+
488
+ docker_images_paginated = self.get_image_list()
489
+ if not docker_images_paginated or not docker_images_paginated.entities:
490
+ typer.echo("No Docker images available")
491
+ raise typer.Exit(1)
492
+
493
+ selected_image = None
494
+ selection_pool = docker_images_paginated.entities
495
+
496
+ if image_name is not None:
497
+ matches = [img for img in selection_pool if img.image == image_name]
498
+
499
+ if len(matches) == 1:
500
+ selected_image = matches[0]
501
+ elif len(matches) > 1:
502
+ typer.echo(f"Multiple tags found for repository '{image_name}':")
503
+ selection_pool = matches
504
+ else:
505
+ typer.echo(f"Image '{image_name}' not found. Select from available images:")
506
+
507
+ if selected_image is None:
508
+ for idx, img in enumerate(selection_pool, start=1):
509
+ typer.echo(f"{idx}) {img.image}:{img.tag}")
510
+
511
+ choice_str = typer.prompt("Choose which image to use")
512
+
513
+ try:
514
+ choice_index = int(choice_str)
515
+ except ValueError:
516
+ typer.echo("Error: Invalid input. Enter a number.")
517
+ raise typer.Exit(1)
518
+
519
+ if not (1 <= choice_index <= len(selection_pool)):
520
+ typer.echo("Error: Choice out of range.")
521
+ raise typer.Exit(1)
522
+
523
+ selected_image = selection_pool[choice_index - 1]
524
+
525
+ request_params = DockerContainerCreateRequest(
526
+ instance_rented_public_id=rented_instance_public_id,
527
+ instance_rented_slug=rented_instance_slug,
528
+ selfhosted_instance_public_id=self_hosted_instance_public_id,
529
+ selfhosted_instance_slug=self_hosted_instance_slug,
530
+ project_public_id=project_public_id,
531
+ project_slug=project_slug,
532
+ container_slug=container_slug,
533
+ docker_image_id=selected_image.id,
534
+ assigned_cpus=cpu_number,
535
+ is_unlimited_cpus=cpu_number is None,
536
+ assigned_gpus=gpu_number,
537
+ is_unlimited_gpus=gpu_number is None,
538
+ mappings=DockerContainerMappings(directory_mappings=volume_mappings)
539
+ )
540
+
541
+ result = self.__docker_container_api_client.create_container(
542
+ request_params
543
+ )
544
+ if result.is_success:
545
+ typer.echo(f'Docker container creation scheduled.')
546
+
547
+ @error_handler()
548
+ def print_docker_image_list(
549
+ self,
550
+ row: int,
551
+ page: int,
552
+ ):
553
+ typer.echo("Listing available Docker images:")
554
+
555
+ self.print(
556
+ func_get_data=self.get_image_list,
557
+ func_special_params={},
558
+ mapper=ImageMapper(),
559
+ headers=list(map(lambda x: x.alias, DockerImageEntity.model_fields.values())),
560
+ row=row,
561
+ page=page,
562
+ max_col_width=[50, 20],
563
+ show_index="never",
564
+ )
565
+
566
+ @error_handler()
567
+ def get_image_list(
568
+ self,
569
+ row: int = 5,
570
+ page: int = 1,
571
+ ) -> PaginatedEntityList[DockerImageDto]:
572
+
573
+ list = self.__docker_container_api_client.get_docker_image_list(
574
+ page=page,
575
+ limit=row,
576
+ )
577
+
578
+ return list
@@ -1,7 +1,7 @@
1
- from typing import Optional, Tuple
1
+ from typing import Optional
2
2
 
3
- from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
4
- from thestage.entities.container import DockerContainerEntity
3
+ from thestage.docker_container.dto.container_response import DockerContainerDto
4
+ from thestage.docker_container.dto.container_entity import DockerContainerEntity
5
5
  from thestage.services.abstract_mapper import AbstractMapper
6
6
 
7
7
 
@@ -0,0 +1,16 @@
1
+ from typing import Optional
2
+
3
+ from thestage.docker_container.dto.container_response import DockerImageDto
4
+ from thestage.docker_container.dto.image_entity import DockerImageEntity
5
+ from thestage.services.abstract_mapper import AbstractMapper
6
+
7
+
8
+ class ImageMapper(AbstractMapper):
9
+ def build_entity(self, item: DockerImageDto) -> Optional[DockerImageEntity]:
10
+ if not item:
11
+ return None
12
+
13
+ return DockerImageEntity(
14
+ image=item.image or '',
15
+ tag=item.tag or '',
16
+ )
File without changes
@@ -1,27 +1,20 @@
1
-
2
- import re
3
1
  from pathlib import Path
4
2
  from typing import Optional, List
5
- from rich import print
3
+
4
+ import typer
6
5
 
7
6
  from thestage.cli_command import CliCommand
8
7
  from thestage.cli_command_helper import get_command_metadata, check_command_permission
9
- from thestage.color_scheme.color_scheme import ColorScheme
10
- from thestage.services.clients.thestage_api.api_client import TheStageApiClient
11
- from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
12
- from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
13
- from thestage.i18n.translation import __
14
- from thestage.services.container.container_service import ContainerService
15
- from thestage.helpers.logger.app_logger import app_logger
16
8
  from thestage.controllers.utils_controller import validate_config_and_get_service_factory, get_current_directory
17
-
18
- import typer
19
-
20
- from thestage.services.logging.logging_service import LoggingService
9
+ from thestage.docker_container.business.container_service import ContainerService
10
+ from thestage.docker_container.dto.container_response import DockerContainerDto
11
+ from thestage.docker_container.dto.enum.container_pending_action import DockerContainerAction
12
+ from thestage.helpers.logger.app_logger import app_logger
13
+ from thestage.i18n.translation import __
14
+ from thestage.logging.business.logging_service import LoggingService
21
15
 
22
16
  app = typer.Typer(no_args_is_help=True, help=__("Manage containers"))
23
17
 
24
-
25
18
  @app.command(name='ls', help=__("List containers"), **get_command_metadata(CliCommand.CONTAINER_LS))
26
19
  def list_containers(
27
20
  row: int = typer.Option(
@@ -65,7 +58,7 @@ def list_containers(
65
58
  check_command_permission(command_name)
66
59
 
67
60
  if sum(v is not None for v in [project_public_id, project_slug]) > 1:
68
- typer.echo("Please provide a single identifier for project - ID or name.")
61
+ typer.echo("Provide a single identifier for project - ID or name.")
69
62
  raise typer.Exit(1)
70
63
 
71
64
  service_factory = validate_config_and_get_service_factory()
@@ -104,7 +97,7 @@ def container_info(
104
97
  check_command_permission(command_name)
105
98
 
106
99
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
107
- typer.echo("Please provide a single identifier for container - ID or name.")
100
+ typer.echo("Provide a single identifier for container - ID or name.")
108
101
  raise typer.Exit(1)
109
102
 
110
103
  service_factory = validate_config_and_get_service_factory()
@@ -194,7 +187,7 @@ def container_connect(
194
187
  check_command_permission(command_name)
195
188
 
196
189
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
197
- typer.echo("Please provide a single identifier for container - ID or name.")
190
+ typer.echo("Provide a single identifier for container - ID or name.")
198
191
  raise typer.Exit(1)
199
192
 
200
193
  if private_ssh_key_path and not Path(private_ssh_key_path).is_file():
@@ -240,7 +233,7 @@ def upload_file(
240
233
  username_param=username,
241
234
  )
242
235
 
243
- app_logger.info(f'End send files to container')
236
+ app_logger.info(f'File upload completed')
244
237
  raise typer.Exit(0)
245
238
 
246
239
 
@@ -269,7 +262,7 @@ def download_file(
269
262
  username_param=username,
270
263
  )
271
264
 
272
- app_logger.info(f'End download files from container')
265
+ app_logger.info(f'File download completed')
273
266
  raise typer.Exit(0)
274
267
 
275
268
 
@@ -295,7 +288,7 @@ def start_container(
295
288
  check_command_permission(command_name)
296
289
 
297
290
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
298
- typer.echo("Please provide a single identifier for container - ID or name.")
291
+ typer.echo("Provide a single identifier for container - ID or name.")
299
292
  raise typer.Exit(1)
300
293
 
301
294
  service_factory = validate_config_and_get_service_factory()
@@ -307,7 +300,140 @@ def start_container(
307
300
  action=DockerContainerAction.START
308
301
  )
309
302
 
310
- app_logger.info(f'End start container')
303
+ app_logger.info(f'Container is starting')
304
+ raise typer.Exit(0)
305
+
306
+
307
+ @app.command(name="create", no_args_is_help=True, help=__("Create container"), **get_command_metadata(CliCommand.CONTAINER_CREATE))
308
+ def create_container(
309
+ project_public_id: Optional[str] = typer.Option(
310
+ None,
311
+ '--project-id',
312
+ '-pid',
313
+ help=__("Project ID. By default, project info is taken from the current directory"),
314
+ is_eager=False,
315
+ ),
316
+ project_slug: Optional[str] = typer.Option(
317
+ None,
318
+ '--project-name',
319
+ '-pn',
320
+ help=__("Project name. By default, project info is taken from the current directory"),
321
+ is_eager=False,
322
+ ),
323
+ working_directory: Optional[str] = typer.Option(
324
+ None,
325
+ "--working-directory",
326
+ "-wd",
327
+ help=__("Full path to working directory"),
328
+ show_default=False,
329
+ is_eager=False,
330
+ ),
331
+ container_slug: Optional[str] = typer.Option(
332
+ None,
333
+ '--container-name',
334
+ '-cn',
335
+ help=__("Container name"),
336
+ is_eager=False,
337
+ ),
338
+ rented_instance_public_id: Optional[str] = typer.Option(
339
+ None,
340
+ '--rented-instance-id',
341
+ '-rid',
342
+ help=__("Rented instance ID on which container will run"),
343
+ is_eager=False,
344
+ ),
345
+ rented_instance_slug: Optional[str] = typer.Option(
346
+ None,
347
+ '--rented-instance-name',
348
+ '-rn',
349
+ help=__("Rented instance name on which container will run"),
350
+ is_eager=False,
351
+ ),
352
+ self_hosted_instance_public_id: Optional[str] = typer.Option(
353
+ None,
354
+ '--self-hosted-instance-id',
355
+ '-sid',
356
+ help=__("Self-hosted instance ID on which container will run"),
357
+ is_eager=False,
358
+ ),
359
+ self_hosted_instance_slug: Optional[str] = typer.Option(
360
+ None,
361
+ '--self-hosted-instance-name',
362
+ '-sn',
363
+ help=__("Self-hosted instance name on which container will run"),
364
+ is_eager=False,
365
+ ),
366
+ image_name: Optional[str] = typer.Option(
367
+ None,
368
+ '--image',
369
+ '-i',
370
+ help=__("Docker image name. If not provided, you will be prompted to select from available images"),
371
+ is_eager=False,
372
+ ),
373
+ gpu_number: Optional[int] = typer.Option(
374
+ None,
375
+ '--gpu-number',
376
+ '-gpun',
377
+ help=__("Number of assigned GPUs for container. No GPU limit by default"),
378
+ is_eager=False,
379
+ ),
380
+ cpu_number: Optional[int] = typer.Option(
381
+ None,
382
+ '--cpu-number',
383
+ '-cpun',
384
+ help=__("Number of assigned CPUs for container. No CPU limit by default"),
385
+ is_eager=False,
386
+ ),
387
+ volume_mounts: Optional[List[str]] = typer.Option(
388
+ None,
389
+ '--volume',
390
+ '-v',
391
+ help=__("Volume mounts. Can be used multiple times. Format: /host_dir:/container_dir"),
392
+ is_eager=False,
393
+ ),
394
+ ):
395
+ command_name = CliCommand.CONTAINER_CREATE
396
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
397
+ check_command_permission(command_name)
398
+
399
+ if sum(v is not None for v in [project_public_id, project_slug]) > 1:
400
+ typer.echo("Provide a single identifier for project - ID or name.")
401
+ raise typer.Exit(1)
402
+
403
+ if sum(v is not None for v in [rented_instance_public_id, rented_instance_slug, self_hosted_instance_public_id, self_hosted_instance_slug]) != 1:
404
+ typer.echo("Provide exactly one identifier (ID or Name) for either a rented or self-hosted instance.")
405
+ raise typer.Exit(1)
406
+
407
+ if not container_slug:
408
+ container_slug = typer.prompt("Enter container name")
409
+
410
+ volume_mappings_dict = {}
411
+ if volume_mounts:
412
+ for mount in volume_mounts:
413
+ if ":" not in mount:
414
+ typer.echo(f"Invalid volume format: {mount}. Expected host:container")
415
+ raise typer.Exit(1)
416
+ host_path, container_path = mount.split(":", 1)
417
+ volume_mappings_dict[host_path] = container_path
418
+
419
+ service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
420
+ container_service: ContainerService = service_factory.get_container_service()
421
+
422
+ container_service.create_docker_container(
423
+ project_public_id=project_public_id,
424
+ project_slug=project_slug,
425
+ container_slug=container_slug,
426
+ rented_instance_public_id=rented_instance_public_id,
427
+ rented_instance_slug=rented_instance_slug,
428
+ self_hosted_instance_public_id=self_hosted_instance_public_id,
429
+ self_hosted_instance_slug=self_hosted_instance_slug,
430
+ image_name=image_name,
431
+ cpu_number=cpu_number,
432
+ gpu_number=gpu_number,
433
+ volume_mappings=volume_mappings_dict
434
+ )
435
+
436
+ app_logger.info(f'Container is creating')
311
437
  raise typer.Exit(0)
312
438
 
313
439
 
@@ -333,7 +459,7 @@ def stop_container(
333
459
  check_command_permission(command_name)
334
460
 
335
461
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
336
- typer.echo("Please provide a single identifier for container - ID or name.")
462
+ typer.echo("Provide a single identifier for container - ID or name.")
337
463
  raise typer.Exit(1)
338
464
 
339
465
  service_factory = validate_config_and_get_service_factory()
@@ -345,10 +471,47 @@ def stop_container(
345
471
  action=DockerContainerAction.STOP
346
472
  )
347
473
 
348
- app_logger.info(f'End stop container')
474
+ app_logger.info(f'Container is stopping')
349
475
  raise typer.Exit(0)
350
476
 
351
477
 
478
+ @app.command(name="delete", no_args_is_help=True, help=__("Delete container"), **get_command_metadata(CliCommand.CONTAINER_DELETE))
479
+ def delete_container(
480
+ container_public_id: str = typer.Option(
481
+ None,
482
+ '--container-id',
483
+ '-cid',
484
+ help=__("Container ID"),
485
+ is_eager=False,
486
+ ),
487
+ container_slug: str = typer.Option(
488
+ None,
489
+ '--container-name',
490
+ '-cn',
491
+ help=__("Container name"),
492
+ is_eager=False,
493
+ ),
494
+ ):
495
+ command_name = CliCommand.CONTAINER_DELETE
496
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
497
+ check_command_permission(command_name)
498
+
499
+ if sum(v is not None for v in [container_public_id, container_slug]) != 1:
500
+ typer.echo("Provide a single identifier for container - ID or name.")
501
+ raise typer.Exit(1)
502
+
503
+ service_factory = validate_config_and_get_service_factory()
504
+ container_service: ContainerService = service_factory.get_container_service()
505
+
506
+ container_service.request_docker_container_action(
507
+ container_public_id=container_public_id,
508
+ container_slug=container_slug,
509
+ action=DockerContainerAction.DELETE
510
+ )
511
+
512
+ app_logger.info(f'Container is deleting')
513
+ raise typer.Exit(0)
514
+
352
515
  @app.command(name="restart", no_args_is_help=True, help=__("Restart container"), **get_command_metadata(CliCommand.CONTAINER_RESTART))
353
516
  def restart_container(
354
517
  container_public_id: str = typer.Option(
@@ -371,7 +534,7 @@ def restart_container(
371
534
  check_command_permission(command_name)
372
535
 
373
536
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
374
- typer.echo("Please provide a single identifier for container - ID or name.")
537
+ typer.echo("Provide a single identifier for container - ID or name.")
375
538
  raise typer.Exit(1)
376
539
 
377
540
  service_factory = validate_config_and_get_service_factory()
@@ -383,7 +546,7 @@ def restart_container(
383
546
  action=DockerContainerAction.RESTART
384
547
  )
385
548
 
386
- app_logger.info(f'End stop container')
549
+ app_logger.info(f'Container is restarting')
387
550
  raise typer.Exit(0)
388
551
 
389
552
 
@@ -416,7 +579,7 @@ def container_logs(
416
579
  check_command_permission(command_name)
417
580
 
418
581
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
419
- typer.echo("Please provide a single identifier for container - ID or name.")
582
+ typer.echo("Provide a single identifier for container - ID or name.")
420
583
  raise typer.Exit(1)
421
584
 
422
585
  service_factory = validate_config_and_get_service_factory()
@@ -430,5 +593,5 @@ def container_logs(
430
593
  else:
431
594
  logging_service.print_last_container_logs(container_public_id=container_public_id, container_slug=container_slug, logs_number=logs_number)
432
595
 
433
- app_logger.info(f'Container logs - end')
434
- raise typer.Exit(0)
596
+ app_logger.info(f'Container log streaming completed')
597
+ raise typer.Exit(0)