thestage 0.5.44__py3-none-any.whl → 0.5.46__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.
- thestage/__init__.py +1 -1
- thestage/controllers/container_controller.py +0 -3
- thestage/controllers/instance_controller.py +8 -75
- thestage/controllers/project_controller.py +3 -121
- thestage/main.py +6 -1
- thestage/services/clients/git/git_client.py +2 -2
- thestage/services/clients/thestage_api/api_client.py +21 -43
- thestage/services/clients/thestage_api/dtos/enums/instance_rented_status.py +1 -0
- thestage/services/clients/thestage_api/dtos/enums/selfhosted_status.py +1 -1
- thestage/services/clients/thestage_api/dtos/{user_profile.py → user_controller/user_profile.py} +6 -3
- thestage/services/config_provider/config_provider.py +10 -29
- thestage/services/connect/connect_service.py +12 -5
- thestage/services/container/container_service.py +2 -2
- thestage/services/core_files/config_entity.py +2 -7
- thestage/services/filesystem_service.py +19 -2
- thestage/services/instance/instance_service.py +101 -7
- thestage/services/project/project_service.py +141 -2
- thestage/services/remote_server_service.py +2 -2
- thestage/services/service_factory.py +4 -4
- thestage/services/validation_service.py +0 -6
- {thestage-0.5.44.dist-info → thestage-0.5.46.dist-info}/METADATA +2 -2
- {thestage-0.5.44.dist-info → thestage-0.5.46.dist-info}/RECORD +25 -26
- {thestage-0.5.44.dist-info → thestage-0.5.46.dist-info}/WHEEL +1 -1
- thestage/services/clients/.DS_Store +0 -0
- {thestage-0.5.44.dist-info → thestage-0.5.46.dist-info}/LICENSE.txt +0 -0
- {thestage-0.5.44.dist-info → thestage-0.5.46.dist-info}/entry_points.txt +0 -0
|
@@ -10,7 +10,7 @@ from thestage.exceptions.file_system_exception import FileSystemException
|
|
|
10
10
|
from thestage.services.core_files.config_entity import ConfigEntity
|
|
11
11
|
from thestage.helpers.ssh_util import parse_private_key
|
|
12
12
|
from thestage.services.connect.dto.remote_server_config import RemoteServerConfig
|
|
13
|
-
from thestage.services.filesystem_service import
|
|
13
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
14
14
|
from thestage.services.project.dto.project_config import ProjectConfig
|
|
15
15
|
from thestage.config import THESTAGE_CONFIG_DIR, THESTAGE_CONFIG_FILE, THESTAGE_AUTH_TOKEN, THESTAGE_API_URL
|
|
16
16
|
|
|
@@ -20,30 +20,27 @@ class ConfigProvider():
|
|
|
20
20
|
_local_config_path: Optional[Path] = None
|
|
21
21
|
_global_config_path: Optional[Path] = None
|
|
22
22
|
_global_config_file: Optional[Path] = None
|
|
23
|
-
_file_system_service =
|
|
23
|
+
_file_system_service = FileSystemService
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def __init__(
|
|
27
27
|
self,
|
|
28
28
|
local_path: Optional[str] = None,
|
|
29
29
|
):
|
|
30
|
-
self._file_system_service =
|
|
30
|
+
self._file_system_service = FileSystemService()
|
|
31
31
|
if local_path:
|
|
32
32
|
self._local_path = self._file_system_service.get_path(directory=local_path, auto_create=False)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
self._local_config_path = self._local_path.joinpath(config_folder_name)
|
|
34
|
+
self._local_config_path = self._local_path.joinpath(THESTAGE_CONFIG_DIR)
|
|
36
35
|
|
|
37
36
|
home_dir = self._file_system_service.get_home_path()
|
|
38
|
-
self._global_config_path = home_dir.joinpath(
|
|
37
|
+
self._global_config_path = home_dir.joinpath(THESTAGE_CONFIG_DIR)
|
|
39
38
|
|
|
40
39
|
if self._global_config_path:
|
|
41
40
|
if not self._global_config_path.exists():
|
|
42
41
|
self._file_system_service.create_if_not_exists_dir(self._global_config_path)
|
|
43
42
|
|
|
44
|
-
self._global_config_file = self._global_config_path.joinpath(
|
|
45
|
-
self._file_system_service.get_path(f"{THESTAGE_CONFIG_FILE}", False)
|
|
46
|
-
)
|
|
43
|
+
self._global_config_file = self._global_config_path.joinpath(THESTAGE_CONFIG_FILE)
|
|
47
44
|
if not self._global_config_file.exists():
|
|
48
45
|
self._file_system_service.create_if_not_exists_file(self._global_config_file)
|
|
49
46
|
|
|
@@ -61,7 +58,7 @@ class ConfigProvider():
|
|
|
61
58
|
if config_from_env:
|
|
62
59
|
self.__update_config_values_dict(values_to_update=config_values, new_values=config_from_env)
|
|
63
60
|
|
|
64
|
-
config_from_file = self.
|
|
61
|
+
config_from_file = self._file_system_service.read_config_file(self._global_config_file)
|
|
65
62
|
if config_from_file:
|
|
66
63
|
self.__update_config_values_dict(values_to_update=config_values, new_values=config_from_file)
|
|
67
64
|
|
|
@@ -112,7 +109,7 @@ class ConfigProvider():
|
|
|
112
109
|
if not project_data_filepath.exists():
|
|
113
110
|
return None
|
|
114
111
|
|
|
115
|
-
config_data = self.
|
|
112
|
+
config_data = self._file_system_service.read_config_file(project_data_filepath) if project_data_filepath and project_data_filepath.exists() else {}
|
|
116
113
|
return ProjectConfig.model_validate(config_data)
|
|
117
114
|
|
|
118
115
|
|
|
@@ -165,7 +162,7 @@ class ConfigProvider():
|
|
|
165
162
|
if not config_filepath.is_file():
|
|
166
163
|
return None
|
|
167
164
|
|
|
168
|
-
config_data = self.
|
|
165
|
+
config_data = self._file_system_service.read_config_file(config_filepath) if config_filepath and config_filepath.exists() else {}
|
|
169
166
|
return RemoteServerConfig.model_validate(config_data)
|
|
170
167
|
|
|
171
168
|
|
|
@@ -188,22 +185,6 @@ class ConfigProvider():
|
|
|
188
185
|
else:
|
|
189
186
|
values_to_update['main'] = new_values['main']
|
|
190
187
|
|
|
191
|
-
|
|
192
|
-
def __read_config_file(self, path: Path) -> Dict[str, Any]:
|
|
193
|
-
result = {}
|
|
194
|
-
try:
|
|
195
|
-
if path and path.exists():
|
|
196
|
-
with path.open("r") as file:
|
|
197
|
-
try:
|
|
198
|
-
if os.stat(path).st_size != 0:
|
|
199
|
-
result = json.load(file)
|
|
200
|
-
except JSONDecodeError:
|
|
201
|
-
pass
|
|
202
|
-
except OSError:
|
|
203
|
-
raise FileSystemException(f"Could not open config file: {path}")
|
|
204
|
-
return result
|
|
205
|
-
|
|
206
|
-
|
|
207
188
|
@staticmethod
|
|
208
189
|
def __save_config_file(data: Dict, file_path: Path):
|
|
209
190
|
with open(file_path, 'w') as configfile:
|
|
@@ -211,7 +192,7 @@ class ConfigProvider():
|
|
|
211
192
|
|
|
212
193
|
|
|
213
194
|
def save_config(self, config: ConfigEntity):
|
|
214
|
-
data: Dict[str, Any] = self.
|
|
195
|
+
data: Dict[str, Any] = self._file_system_service.read_config_file(self._global_config_file)
|
|
215
196
|
data.update(config.model_dump(exclude_none=True, by_alias=True, exclude={'runtime', 'RUNTIME', 'daemon', 'DAEMON'}))
|
|
216
197
|
self.__save_config_file(data=data, file_path=self._global_config_file)
|
|
217
198
|
|
|
@@ -50,20 +50,25 @@ class ConnectService(AbstractService):
|
|
|
50
50
|
):
|
|
51
51
|
config = self._config_provider.get_full_config()
|
|
52
52
|
|
|
53
|
-
# TODO make a single separate method that will return all needed info
|
|
54
53
|
try:
|
|
55
|
-
instance_selfhosted = self.__thestage_api_client.
|
|
54
|
+
instance_selfhosted = self.__thestage_api_client.get_selfhosted_instance(token=config.main.thestage_auth_token, instance_slug=uid)
|
|
56
55
|
except HttpClientException as e:
|
|
56
|
+
if e.get_status_code() == 403:
|
|
57
|
+
typer.echo("Missing permission to view self-hosted instances")
|
|
57
58
|
instance_selfhosted = None
|
|
58
59
|
|
|
59
60
|
try:
|
|
60
|
-
instance_rented = self.__thestage_api_client.
|
|
61
|
+
instance_rented = self.__thestage_api_client.get_rented_instance(token=config.main.thestage_auth_token, instance_slug=uid)
|
|
61
62
|
except HttpClientException as e:
|
|
63
|
+
if e.get_status_code() == 403:
|
|
64
|
+
typer.echo("Missing permission to view rented instances")
|
|
62
65
|
instance_rented = None
|
|
63
66
|
|
|
64
67
|
try:
|
|
65
68
|
container = self.__thestage_api_client.get_container(token=config.main.thestage_auth_token, container_slug=uid, )
|
|
66
|
-
except
|
|
69
|
+
except HttpClientException as e:
|
|
70
|
+
if e.get_status_code() == 403:
|
|
71
|
+
typer.echo("Missing permission to view containers")
|
|
67
72
|
container = None
|
|
68
73
|
|
|
69
74
|
task: Optional[TaskDto] = None
|
|
@@ -71,6 +76,8 @@ class ConnectService(AbstractService):
|
|
|
71
76
|
try:
|
|
72
77
|
task_view_response = self.__thestage_api_client.get_task(token=config.main.thestage_auth_token, task_id=int(uid))
|
|
73
78
|
except HttpClientException as e:
|
|
79
|
+
if e.get_status_code() == 403:
|
|
80
|
+
typer.echo("Missing permission to view tasks")
|
|
74
81
|
task_view_response = None
|
|
75
82
|
if task_view_response and task_view_response.task:
|
|
76
83
|
task = task_view_response.task
|
|
@@ -148,7 +155,7 @@ class ConnectService(AbstractService):
|
|
|
148
155
|
instance_rented: Optional[InstanceRentedDto] = None
|
|
149
156
|
if instance_slug:
|
|
150
157
|
try:
|
|
151
|
-
instance_rented = self.__thestage_api_client.
|
|
158
|
+
instance_rented = self.__thestage_api_client.get_rented_instance(token=config.main.thestage_auth_token, instance_slug=instance_slug)
|
|
152
159
|
except HttpClientException as e:
|
|
153
160
|
instance_rented = None
|
|
154
161
|
|
|
@@ -10,7 +10,7 @@ from thestage.services.clients.thestage_api.dtos.enums.container_status import D
|
|
|
10
10
|
from thestage.entities.enums.shell_type import ShellType
|
|
11
11
|
from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
|
|
12
12
|
from thestage.services.container.mapper.container_mapper import ContainerMapper
|
|
13
|
-
from thestage.services.filesystem_service import
|
|
13
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
14
14
|
from thestage.services.remote_server_service import RemoteServerService
|
|
15
15
|
from thestage.i18n.translation import __
|
|
16
16
|
from thestage.services.abstract_service import AbstractService
|
|
@@ -29,7 +29,7 @@ class ContainerService(AbstractService):
|
|
|
29
29
|
thestage_api_client: TheStageApiClient,
|
|
30
30
|
config_provider: ConfigProvider,
|
|
31
31
|
remote_server_service: RemoteServerService,
|
|
32
|
-
file_system_service:
|
|
32
|
+
file_system_service: FileSystemService,
|
|
33
33
|
):
|
|
34
34
|
super(ContainerService, self).__init__(
|
|
35
35
|
config_provider=config_provider
|
|
@@ -7,11 +7,6 @@ class MainConfigEntity(BaseModel):
|
|
|
7
7
|
thestage_auth_token: Optional[str] = Field(None, alias='thestage_auth_token')
|
|
8
8
|
thestage_api_url: Optional[str] = Field(None, alias='thestage_api_url')
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
class DaemonConfigEntity(BaseModel):
|
|
12
|
-
daemon_token: Optional[str] = Field(None, alias='daemon_token')
|
|
13
|
-
backend_api_url: Optional[str] = Field(None, alias='backend_api_url')
|
|
14
|
-
|
|
15
10
|
# not saved to file
|
|
16
11
|
class RuntimeConfigEntity(BaseModel):
|
|
17
12
|
working_directory: Optional[str] = Field(None, alias='working_directory')
|
|
@@ -19,7 +14,7 @@ class RuntimeConfigEntity(BaseModel):
|
|
|
19
14
|
|
|
20
15
|
|
|
21
16
|
class ConfigEntity(BaseModel):
|
|
17
|
+
global_config_path: str = Field(None, alias='global_config_path')
|
|
18
|
+
can_use_inference: bool = Field(None, alias='can_use_inference')
|
|
22
19
|
main: MainConfigEntity = Field(default_factory=MainConfigEntity, alias='main')
|
|
23
20
|
runtime: RuntimeConfigEntity = Field(default_factory=RuntimeConfigEntity, alias="runtime") # TODO merge with main
|
|
24
|
-
daemon: DaemonConfigEntity = Field(default_factory=DaemonConfigEntity, alias="daemon") # TODO this should not be in core package
|
|
25
|
-
start_on_daemon: bool = Field(False, alias='start_on_daemon') # TODO this should not be in core package
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import os
|
|
2
3
|
import shutil
|
|
4
|
+
from json import JSONDecodeError
|
|
3
5
|
from pathlib import Path
|
|
4
|
-
from typing import Optional, List
|
|
6
|
+
from typing import Optional, List, Dict, Any
|
|
5
7
|
|
|
6
8
|
from thestage.entities.file_item import FileItemEntity
|
|
7
9
|
from thestage.exceptions.file_system_exception import FileSystemException
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
class
|
|
12
|
+
class FileSystemService:
|
|
11
13
|
|
|
12
14
|
def get_ssh_path(self) -> Optional[Path]:
|
|
13
15
|
home_path = self.get_home_path()
|
|
@@ -114,3 +116,18 @@ class FileSystemServiceCore:
|
|
|
114
116
|
real_path = self.get_path(directory=path, auto_create=False)
|
|
115
117
|
if real_path and real_path.exists():
|
|
116
118
|
shutil.rmtree(real_path)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def read_config_file(self, path: Path) -> Dict[str, Any]:
|
|
122
|
+
result = {}
|
|
123
|
+
try:
|
|
124
|
+
if path and path.exists():
|
|
125
|
+
with path.open("r") as file:
|
|
126
|
+
try:
|
|
127
|
+
if os.stat(path).st_size != 0:
|
|
128
|
+
result = json.load(file)
|
|
129
|
+
except JSONDecodeError:
|
|
130
|
+
pass
|
|
131
|
+
except OSError:
|
|
132
|
+
raise FileSystemException(f"Could not open config file: {path}")
|
|
133
|
+
return result
|
|
@@ -2,6 +2,9 @@ from pathlib import Path
|
|
|
2
2
|
from typing import List, Optional, Dict
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
|
+
|
|
6
|
+
from thestage.entities.rented_instance import RentedInstanceEntity
|
|
7
|
+
from thestage.entities.self_hosted_instance import SelfHostedInstanceEntity
|
|
5
8
|
from thestage.services.core_files.config_entity import ConfigEntity
|
|
6
9
|
from thestage.i18n.translation import __
|
|
7
10
|
from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
|
|
@@ -13,6 +16,8 @@ from thestage.services.clients.thestage_api.dtos.instance_rented_response import
|
|
|
13
16
|
from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
|
|
14
17
|
from thestage.services.clients.thestage_api.dtos.selfhosted_instance_response import SelfHostedInstanceDto
|
|
15
18
|
from thestage.services.config_provider.config_provider import ConfigProvider
|
|
19
|
+
from thestage.services.instance.mapper.instance_mapper import InstanceMapper
|
|
20
|
+
from thestage.services.instance.mapper.selfhosted_mapper import SelfHostedMapper
|
|
16
21
|
from thestage.services.remote_server_service import RemoteServerService
|
|
17
22
|
|
|
18
23
|
|
|
@@ -32,22 +37,22 @@ class InstanceService(AbstractService):
|
|
|
32
37
|
self.__thestage_api_client = thestage_api_client
|
|
33
38
|
self.__remote_server_service = remote_server_service
|
|
34
39
|
|
|
35
|
-
def
|
|
40
|
+
def get_rented_instance(
|
|
36
41
|
self,
|
|
37
42
|
config: ConfigEntity,
|
|
38
43
|
instance_slug: str,
|
|
39
44
|
) -> Optional[InstanceRentedDto]:
|
|
40
|
-
return self.__thestage_api_client.
|
|
45
|
+
return self.__thestage_api_client.get_rented_instance(
|
|
41
46
|
token=config.main.thestage_auth_token,
|
|
42
47
|
instance_slug=instance_slug,
|
|
43
48
|
)
|
|
44
49
|
|
|
45
|
-
def
|
|
50
|
+
def get_self_hosted_instance(
|
|
46
51
|
self,
|
|
47
52
|
config: ConfigEntity,
|
|
48
53
|
instance_slug: str,
|
|
49
54
|
) -> Optional[SelfHostedInstanceDto]:
|
|
50
|
-
return self.__thestage_api_client.
|
|
55
|
+
return self.__thestage_api_client.get_selfhosted_instance(
|
|
51
56
|
token=config.main.thestage_auth_token,
|
|
52
57
|
instance_slug=instance_slug,
|
|
53
58
|
)
|
|
@@ -100,7 +105,7 @@ class InstanceService(AbstractService):
|
|
|
100
105
|
typer.echo(__('Cannot connect to self-hosted instance: it is awaiting configuration'))
|
|
101
106
|
raise typer.Exit(1)
|
|
102
107
|
elif instance.frontend_status.status_key in [
|
|
103
|
-
SelfhostedBusinessStatus.
|
|
108
|
+
SelfhostedBusinessStatus.UNREACHABLE_DAEMON.name,
|
|
104
109
|
SelfhostedBusinessStatus.DELETED.name,
|
|
105
110
|
]:
|
|
106
111
|
typer.echo(__('Cannot connect to self-hosted instance: it may be turned off or unreachable'))
|
|
@@ -121,7 +126,7 @@ class InstanceService(AbstractService):
|
|
|
121
126
|
config: ConfigEntity,
|
|
122
127
|
input_ssh_key_path: Optional[str]
|
|
123
128
|
):
|
|
124
|
-
instance = self.
|
|
129
|
+
instance = self.get_rented_instance(config=config, instance_slug=instance_rented_slug)
|
|
125
130
|
|
|
126
131
|
if instance:
|
|
127
132
|
self.check_instance_status_to_connect(
|
|
@@ -162,7 +167,7 @@ class InstanceService(AbstractService):
|
|
|
162
167
|
username = 'root'
|
|
163
168
|
typer.echo(__("No remote server username provided, using 'root' as username"))
|
|
164
169
|
|
|
165
|
-
instance = self.
|
|
170
|
+
instance = self.get_self_hosted_instance(config=config, instance_slug=selfhosted_instance_slug)
|
|
166
171
|
|
|
167
172
|
if instance:
|
|
168
173
|
self.check_selfhosted_status_to_connect(
|
|
@@ -222,3 +227,92 @@ class InstanceService(AbstractService):
|
|
|
222
227
|
limit=row,
|
|
223
228
|
)
|
|
224
229
|
return data
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@error_handler()
|
|
233
|
+
def print_self_hosted_instance_list(self, config: ConfigEntity, statuses, row, page):
|
|
234
|
+
selfhosted_instance_status_map = self.__thestage_api_client.get_selfhosted_business_status_map(config.main.thestage_auth_token)
|
|
235
|
+
|
|
236
|
+
if not statuses:
|
|
237
|
+
statuses = ({key: selfhosted_instance_status_map[key] for key in [
|
|
238
|
+
SelfhostedBusinessStatus.AWAITING_CONFIGURATION,
|
|
239
|
+
SelfhostedBusinessStatus.RUNNING,
|
|
240
|
+
SelfhostedBusinessStatus.UNREACHABLE_DAEMON,
|
|
241
|
+
]}).values()
|
|
242
|
+
|
|
243
|
+
if "all" in statuses:
|
|
244
|
+
statuses = selfhosted_instance_status_map.values()
|
|
245
|
+
|
|
246
|
+
for input_status_item in statuses:
|
|
247
|
+
if input_status_item not in selfhosted_instance_status_map.values():
|
|
248
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
249
|
+
'invalid_status': input_status_item,
|
|
250
|
+
'valid_statuses': str(list(selfhosted_instance_status_map.values()))
|
|
251
|
+
}))
|
|
252
|
+
raise typer.Exit(1)
|
|
253
|
+
|
|
254
|
+
typer.echo(__(
|
|
255
|
+
"Listing self-hosted instances with the following statuses: %statuses%, to view all self-hosted instances, use --status all",
|
|
256
|
+
placeholders={
|
|
257
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
258
|
+
}))
|
|
259
|
+
|
|
260
|
+
backend_statuses: List[str] = [key for key, value in selfhosted_instance_status_map.items() if value in statuses]
|
|
261
|
+
|
|
262
|
+
self.print(
|
|
263
|
+
func_get_data=self.get_self_hosted_list,
|
|
264
|
+
func_special_params={
|
|
265
|
+
'statuses': backend_statuses,
|
|
266
|
+
},
|
|
267
|
+
mapper=SelfHostedMapper(),
|
|
268
|
+
config=config,
|
|
269
|
+
headers=list(map(lambda x: x.alias, SelfHostedInstanceEntity.model_fields.values())),
|
|
270
|
+
row=row,
|
|
271
|
+
page=page,
|
|
272
|
+
show_index="never",
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@error_handler()
|
|
277
|
+
def print_rented_instance_list(self, config: ConfigEntity, statuses, row, page):
|
|
278
|
+
instance_rented_status_map = self.__thestage_api_client.get_rented_business_status_map(config.main.thestage_auth_token)
|
|
279
|
+
|
|
280
|
+
if not statuses:
|
|
281
|
+
statuses = ({key: instance_rented_status_map[key] for key in [
|
|
282
|
+
InstanceRentedBusinessStatus.ONLINE,
|
|
283
|
+
InstanceRentedBusinessStatus.CREATING,
|
|
284
|
+
InstanceRentedBusinessStatus.TERMINATING,
|
|
285
|
+
InstanceRentedBusinessStatus.REBOOTING,
|
|
286
|
+
]}).values()
|
|
287
|
+
|
|
288
|
+
if "all" in statuses:
|
|
289
|
+
statuses = instance_rented_status_map.values()
|
|
290
|
+
|
|
291
|
+
for input_status_item in statuses:
|
|
292
|
+
if input_status_item not in instance_rented_status_map.values():
|
|
293
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
294
|
+
'invalid_status': input_status_item,
|
|
295
|
+
'valid_statuses': str(list(instance_rented_status_map.values()))
|
|
296
|
+
}))
|
|
297
|
+
raise typer.Exit(1)
|
|
298
|
+
|
|
299
|
+
typer.echo(__(
|
|
300
|
+
"Listing rented server instances with the following statuses: %statuses%, to view all rented server instances, use --status all",
|
|
301
|
+
placeholders={
|
|
302
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
303
|
+
}))
|
|
304
|
+
|
|
305
|
+
backend_statuses: List[str] = [key for key, value in instance_rented_status_map.items() if value in statuses]
|
|
306
|
+
|
|
307
|
+
self.print(
|
|
308
|
+
func_get_data=self.get_rented_list,
|
|
309
|
+
func_special_params={
|
|
310
|
+
'statuses': backend_statuses,
|
|
311
|
+
},
|
|
312
|
+
mapper=InstanceMapper(),
|
|
313
|
+
config=config,
|
|
314
|
+
headers=list(map(lambda x: x.alias, RentedInstanceEntity.model_fields.values())),
|
|
315
|
+
row=row,
|
|
316
|
+
page=page,
|
|
317
|
+
show_index="never",
|
|
318
|
+
)
|
|
@@ -12,7 +12,12 @@ import typer
|
|
|
12
12
|
from git import Commit
|
|
13
13
|
from tabulate import tabulate
|
|
14
14
|
|
|
15
|
+
from thestage.entities.project_inference_simulator import ProjectInferenceSimulatorEntity
|
|
16
|
+
from thestage.entities.project_inference_simulator_model import ProjectInferenceSimulatorModelEntity
|
|
17
|
+
from thestage.entities.project_task import ProjectTaskEntity
|
|
15
18
|
from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
|
|
19
|
+
from thestage.services.clients.thestage_api.dtos.enums.inference_model_status import InferenceModelStatus
|
|
20
|
+
from thestage.services.clients.thestage_api.dtos.enums.inference_simulator_status import InferenceSimulatorStatus
|
|
16
21
|
from thestage.services.core_files.config_entity import ConfigEntity
|
|
17
22
|
from thestage.color_scheme.color_scheme import ColorScheme
|
|
18
23
|
from thestage.entities.enums.yes_no_response import YesOrNoResponse
|
|
@@ -36,9 +41,12 @@ from thestage.services.clients.thestage_api.dtos.project_controller.project_star
|
|
|
36
41
|
ProjectStartInferenceSimulatorResponse
|
|
37
42
|
from thestage.services.clients.thestage_api.dtos.project_response import ProjectDto
|
|
38
43
|
from thestage.services.clients.thestage_api.dtos.task_controller.task_view_response import TaskViewResponse
|
|
39
|
-
from thestage.services.filesystem_service import
|
|
44
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
40
45
|
from thestage.services.project.dto.inference_simulator_dto import InferenceSimulatorDto
|
|
41
46
|
from thestage.services.project.dto.inference_simulator_model_dto import InferenceSimulatorModelDto
|
|
47
|
+
from thestage.services.project.mapper.project_inference_simulator_mapper import ProjectInferenceSimulatorMapper
|
|
48
|
+
from thestage.services.project.mapper.project_inference_simulator_model_mapper import \
|
|
49
|
+
ProjectInferenceSimulatorModelMapper
|
|
42
50
|
from thestage.services.task.dto.task_dto import TaskDto
|
|
43
51
|
from thestage.services.project.dto.project_config import ProjectConfig
|
|
44
52
|
from thestage.services.project.mapper.project_task_mapper import ProjectTaskMapper
|
|
@@ -58,7 +66,7 @@ class ProjectService(AbstractService):
|
|
|
58
66
|
thestage_api_client: TheStageApiClient,
|
|
59
67
|
config_provider: ConfigProvider,
|
|
60
68
|
remote_server_service: RemoteServerService,
|
|
61
|
-
file_system_service:
|
|
69
|
+
file_system_service: FileSystemService,
|
|
62
70
|
git_local_client: GitLocalClient,
|
|
63
71
|
):
|
|
64
72
|
super(ProjectService, self).__init__(
|
|
@@ -1139,3 +1147,134 @@ class ProjectService(AbstractService):
|
|
|
1139
1147
|
deploy_key_path=project_config.deploy_key_path,
|
|
1140
1148
|
reset_to_origin=True
|
|
1141
1149
|
)
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
@error_handler()
|
|
1153
|
+
def print_inference_simulator_list(self, config, project_uid, statuses, row, page):
|
|
1154
|
+
if not project_uid:
|
|
1155
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1156
|
+
if not project_config:
|
|
1157
|
+
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
1158
|
+
raise typer.Exit(1)
|
|
1159
|
+
project_uid = project_config.slug
|
|
1160
|
+
|
|
1161
|
+
inference_simulator_status_map = self.__thestage_api_client.get_inference_simulator_business_status_map(
|
|
1162
|
+
config.main.thestage_auth_token)
|
|
1163
|
+
|
|
1164
|
+
if not statuses:
|
|
1165
|
+
statuses = ({key: inference_simulator_status_map[key] for key in [
|
|
1166
|
+
InferenceSimulatorStatus.SCHEDULED,
|
|
1167
|
+
InferenceSimulatorStatus.CREATING,
|
|
1168
|
+
InferenceSimulatorStatus.RUNNING,
|
|
1169
|
+
]}).values()
|
|
1170
|
+
|
|
1171
|
+
if "all" in statuses:
|
|
1172
|
+
statuses = inference_simulator_status_map.values()
|
|
1173
|
+
|
|
1174
|
+
for input_status_item in statuses:
|
|
1175
|
+
if input_status_item not in inference_simulator_status_map.values():
|
|
1176
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
1177
|
+
'invalid_status': input_status_item,
|
|
1178
|
+
'valid_statuses': str(list(inference_simulator_status_map.values()))
|
|
1179
|
+
}))
|
|
1180
|
+
raise typer.Exit(1)
|
|
1181
|
+
|
|
1182
|
+
typer.echo(__(
|
|
1183
|
+
"Listing inference simulators with the following statuses: %statuses%, to view all inference simulators, use --status all",
|
|
1184
|
+
placeholders={
|
|
1185
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
1186
|
+
}))
|
|
1187
|
+
|
|
1188
|
+
backend_statuses: List[str] = [key for key, value in inference_simulator_status_map.items() if value in statuses]
|
|
1189
|
+
|
|
1190
|
+
self.print(
|
|
1191
|
+
func_get_data=self.get_project_inference_simulator_list,
|
|
1192
|
+
func_special_params={
|
|
1193
|
+
'project_slug': project_uid,
|
|
1194
|
+
'statuses': backend_statuses,
|
|
1195
|
+
},
|
|
1196
|
+
mapper=ProjectInferenceSimulatorMapper(),
|
|
1197
|
+
config=config,
|
|
1198
|
+
headers=list(map(lambda x: x.alias, ProjectInferenceSimulatorEntity.model_fields.values())),
|
|
1199
|
+
row=row,
|
|
1200
|
+
page=page,
|
|
1201
|
+
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
1202
|
+
show_index="never",
|
|
1203
|
+
)
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
@error_handler()
|
|
1207
|
+
def print_inference_simulator_model_list(self, config, project_uid, statuses, row, page):
|
|
1208
|
+
if not project_uid:
|
|
1209
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1210
|
+
if not project_config:
|
|
1211
|
+
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
1212
|
+
raise typer.Exit(1)
|
|
1213
|
+
project_uid = project_config.slug
|
|
1214
|
+
|
|
1215
|
+
inference_simulator_model_status_map = self.__thestage_api_client.get_inference_simulator_model_business_status_map(
|
|
1216
|
+
config.main.thestage_auth_token)
|
|
1217
|
+
|
|
1218
|
+
if not statuses:
|
|
1219
|
+
statuses = ({key: inference_simulator_model_status_map[key] for key in [
|
|
1220
|
+
InferenceModelStatus.SCHEDULED,
|
|
1221
|
+
InferenceModelStatus.PROCESSING,
|
|
1222
|
+
InferenceModelStatus.PUSH_SUCCEED,
|
|
1223
|
+
]}).values()
|
|
1224
|
+
|
|
1225
|
+
if "all" in statuses:
|
|
1226
|
+
statuses = inference_simulator_model_status_map.values()
|
|
1227
|
+
|
|
1228
|
+
for input_status_item in statuses:
|
|
1229
|
+
if input_status_item not in inference_simulator_model_status_map.values():
|
|
1230
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
1231
|
+
'invalid_status': input_status_item,
|
|
1232
|
+
'valid_statuses': str(list(inference_simulator_model_status_map.values()))
|
|
1233
|
+
}))
|
|
1234
|
+
raise typer.Exit(1)
|
|
1235
|
+
|
|
1236
|
+
typer.echo(__(
|
|
1237
|
+
"Listing inference simulator models with the following statuses: %statuses%, to view all inference simulator models, use --status all",
|
|
1238
|
+
placeholders={
|
|
1239
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
1240
|
+
}))
|
|
1241
|
+
|
|
1242
|
+
backend_statuses: List[str] = [key for key, value in inference_simulator_model_status_map.items() if value in statuses]
|
|
1243
|
+
|
|
1244
|
+
self.print(
|
|
1245
|
+
func_get_data=self.get_project_inference_simulator_model_list,
|
|
1246
|
+
func_special_params={
|
|
1247
|
+
'project_slug': project_uid,
|
|
1248
|
+
'statuses': backend_statuses,
|
|
1249
|
+
},
|
|
1250
|
+
mapper=ProjectInferenceSimulatorModelMapper(),
|
|
1251
|
+
config=config,
|
|
1252
|
+
headers=list(map(lambda x: x.alias, ProjectInferenceSimulatorModelEntity.model_fields.values())),
|
|
1253
|
+
row=row,
|
|
1254
|
+
page=page,
|
|
1255
|
+
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
1256
|
+
show_index="never",
|
|
1257
|
+
)
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def print_task_list(self, config: ConfigEntity, project_uid, row, page):
|
|
1261
|
+
if not project_uid:
|
|
1262
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1263
|
+
if not project_config:
|
|
1264
|
+
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
1265
|
+
raise typer.Exit(1)
|
|
1266
|
+
project_uid = project_config.slug
|
|
1267
|
+
|
|
1268
|
+
self.print(
|
|
1269
|
+
func_get_data=self.get_project_task_list,
|
|
1270
|
+
func_special_params={
|
|
1271
|
+
'project_slug': project_uid,
|
|
1272
|
+
},
|
|
1273
|
+
mapper=ProjectTaskMapper(),
|
|
1274
|
+
config=config,
|
|
1275
|
+
headers=list(map(lambda x: x.alias, ProjectTaskEntity.model_fields.values())),
|
|
1276
|
+
row=row,
|
|
1277
|
+
page=page,
|
|
1278
|
+
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
1279
|
+
show_index="never",
|
|
1280
|
+
)
|
|
@@ -21,7 +21,7 @@ from thestage.helpers.ssh_util import parse_private_key
|
|
|
21
21
|
from thestage.i18n.translation import __
|
|
22
22
|
from thestage.services.clients.thestage_api.dtos.sftp_path_helper import SftpFileItemEntity
|
|
23
23
|
from thestage.services.config_provider.config_provider import ConfigProvider
|
|
24
|
-
from thestage.services.filesystem_service import
|
|
24
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
25
25
|
|
|
26
26
|
old_value: int = 0
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ class RemoteServerService:
|
|
|
30
30
|
|
|
31
31
|
def __init__(
|
|
32
32
|
self,
|
|
33
|
-
file_system_service:
|
|
33
|
+
file_system_service: FileSystemService,
|
|
34
34
|
config_provider: ConfigProvider,
|
|
35
35
|
):
|
|
36
36
|
self.__file_system_service = file_system_service
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
3
|
from thestage.services.connect.connect_service import ConnectService
|
|
4
|
-
from thestage.services.filesystem_service import
|
|
4
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
5
5
|
from thestage.services.logging.logging_service import LoggingService
|
|
6
6
|
from thestage.services.project.project_service import ProjectService
|
|
7
7
|
from thestage.services.remote_server_service import RemoteServerService
|
|
@@ -18,7 +18,7 @@ class ServiceFactory:
|
|
|
18
18
|
__config_provider: Optional[ConfigProvider] = None
|
|
19
19
|
__thestage_api_client: Optional[TheStageApiClient] = None
|
|
20
20
|
__git_local_client: Optional[GitLocalClient] = None
|
|
21
|
-
__file_system_service: Optional[
|
|
21
|
+
__file_system_service: Optional[FileSystemService] = None
|
|
22
22
|
|
|
23
23
|
def __init__(
|
|
24
24
|
self,
|
|
@@ -84,9 +84,9 @@ class ServiceFactory:
|
|
|
84
84
|
self.__git_local_client = GitLocalClient(file_system_service=self.get_file_system_service())
|
|
85
85
|
return self.__git_local_client
|
|
86
86
|
|
|
87
|
-
def get_file_system_service(self) ->
|
|
87
|
+
def get_file_system_service(self) -> FileSystemService:
|
|
88
88
|
if not self.__file_system_service:
|
|
89
|
-
self.__file_system_service =
|
|
89
|
+
self.__file_system_service = FileSystemService()
|
|
90
90
|
return self.__file_system_service
|
|
91
91
|
|
|
92
92
|
def get_app_config_service(self, config_provider: Optional[ConfigProvider] = None,) -> AppConfigService:
|
|
@@ -53,10 +53,4 @@ class ValidationService:
|
|
|
53
53
|
if not config.main.thestage_auth_token:
|
|
54
54
|
present_token = False
|
|
55
55
|
|
|
56
|
-
if config.start_on_daemon:
|
|
57
|
-
if config.daemon and config.daemon.daemon_token:
|
|
58
|
-
present_token = True
|
|
59
|
-
else:
|
|
60
|
-
present_token = False
|
|
61
|
-
|
|
62
56
|
return present_token
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: thestage
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.46
|
|
4
4
|
Summary:
|
|
5
5
|
Author: TheStage AI team
|
|
6
6
|
Author-email: hello@thestage.ai
|
|
@@ -21,7 +21,7 @@ Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
|
21
21
|
Requires-Dist: python-gettext-translations (>=1.1.0,<2.0.0)
|
|
22
22
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
23
23
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
24
|
-
Requires-Dist: typer[all] (>=0.
|
|
24
|
+
Requires-Dist: typer[all] (>=0.15.2,<0.16.0)
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
|
|
27
27
|
# Introduction
|