thestage 0.5.46__py3-none-any.whl → 0.5.47__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 (49) hide show
  1. thestage/.env +5 -0
  2. thestage/__init__.py +2 -1
  3. thestage/cli_command.py +56 -0
  4. thestage/cli_command_helper.py +51 -0
  5. thestage/config/config_storage.py +3 -0
  6. thestage/controllers/base_controller.py +19 -15
  7. thestage/controllers/config_controller.py +30 -38
  8. thestage/controllers/container_controller.py +43 -79
  9. thestage/controllers/instance_controller.py +23 -40
  10. thestage/controllers/project_controller.py +95 -184
  11. thestage/controllers/utils_controller.py +12 -8
  12. thestage/debug_tests.py +12 -0
  13. thestage/entities/container.py +0 -5
  14. thestage/entities/rented_instance.py +0 -5
  15. thestage/entities/self_hosted_instance.py +0 -2
  16. thestage/helpers/error_handler.py +14 -14
  17. thestage/helpers/exception_hook.py +14 -0
  18. thestage/helpers/logger/app_logger.py +3 -4
  19. thestage/main.py +25 -13
  20. thestage/services/abstract_service.py +0 -40
  21. thestage/services/app_config_service.py +7 -6
  22. thestage/services/clients/.DS_Store +0 -0
  23. thestage/services/clients/thestage_api/api_client.py +54 -68
  24. thestage/services/clients/thestage_api/core/api_client_core.py +92 -9
  25. thestage/services/clients/thestage_api/dtos/container_response.py +1 -1
  26. thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +1 -1
  27. thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +1 -1
  28. thestage/services/clients/thestage_api/dtos/instance_rented_response.py +1 -1
  29. thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +1 -1
  30. thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +1 -1
  31. thestage/services/clients/thestage_api/dtos/validate_token_response.py +11 -0
  32. thestage/services/config_provider/config_provider.py +75 -47
  33. thestage/services/connect/connect_service.py +11 -22
  34. thestage/services/container/container_service.py +6 -23
  35. thestage/services/core_files/config_entity.py +7 -1
  36. thestage/services/filesystem_service.py +2 -2
  37. thestage/services/instance/instance_service.py +12 -26
  38. thestage/services/logging/logging_service.py +17 -45
  39. thestage/services/project/project_service.py +33 -72
  40. thestage/services/remote_server_service.py +3 -4
  41. thestage/services/service_factory.py +21 -27
  42. thestage/services/validation_service.py +14 -9
  43. {thestage-0.5.46.dist-info → thestage-0.5.47.dist-info}/METADATA +3 -4
  44. {thestage-0.5.46.dist-info → thestage-0.5.47.dist-info}/RECORD +47 -41
  45. {thestage-0.5.46.dist-info → thestage-0.5.47.dist-info}/WHEEL +1 -1
  46. thestage/exceptions/http_error_exception.py +0 -12
  47. thestage/services/clients/thestage_api/core/api_client_abstract.py +0 -91
  48. {thestage-0.5.46.dist-info → thestage-0.5.47.dist-info}/LICENSE.txt +0 -0
  49. {thestage-0.5.46.dist-info → thestage-0.5.47.dist-info}/entry_points.txt +0 -0
thestage/.env ADDED
@@ -0,0 +1,5 @@
1
+ ; THESTAGE_CONFIG_DIR=.thestage
2
+ ; THESTAGE_CONFIG_FILE=config.json
3
+ ; THESTAGE_API_URL=https://backend-staging3.thestage.ai
4
+ ; THESTAGE_API_URL=http://localhost:8100
5
+ ; LOG_FILE=thestage.log
thestage/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
1
  from . import *
2
2
  __app_name__ = "thestage"
3
- __version__ = "0.5.46"
3
+
4
+ __version__ = "0.5.47"
@@ -0,0 +1,56 @@
1
+ from enum import Enum
2
+
3
+ class CliCommand(str, Enum):
4
+ VERSION = "VERSION"
5
+ CONNECT = "CONNECT"
6
+ PROJECT_CLONE = "PROJECT_CLONE"
7
+ PROJECT_INIT = "PROJECT_INIT"
8
+ PROJECT_RUN = "PROJECT_RUN"
9
+ PROJECT_CHECKOUT = "PROJECT_CHECKOUT"
10
+ PROJECT_PULL = "PROJECT_PULL"
11
+ PROJECT_RESET = "PROJECT_RESET"
12
+ PROJECT_INFERENCE_SIMULATOR_RUN = "PROJECT_INFERENCE_SIMULATOR_RUN"
13
+ PROJECT_INFERENCE_SIMULATOR_SAVE_METADATA = "PROJECT_INFERENCE_SIMULATOR_SAVE_METADATA"
14
+ PROJECT_INFERENCE_SIMULATOR_PUSH = "PROJECT_INFERENCE_SIMULATOR_PUSH"
15
+ PROJECT_INFERENCE_SIMULATOR_LS = "PROJECT_INFERENCE_SIMULATOR_LS"
16
+ PROJECT_INFERENCE_SIMULATOR_LOGS = "PROJECT_INFERENCE_SIMULATOR_LOGS"
17
+ PROJECT_MODEL_LS = "PROJECT_MODEL_LS"
18
+ PROJECT_MODEL_DEPLOY_INSTANCE = "PROJECT_MODEL_DEPLOY_INSTANCE"
19
+ PROJECT_MODEL_DEPLOY_SAGEMAKER = "PROJECT_MODEL_DEPLOY_SAGEMAKER"
20
+ PROJECT_TASK_CANCEL = "PROJECT_TASK_CANCEL"
21
+ PROJECT_TASK_LS = "PROJECT_TASK_LS"
22
+ PROJECT_TASK_LOGS = "PROJECT_TASK_LOGS"
23
+ PROJECT_CONFIG_SET_DEFAULT_CONTAINER = "PROJECT_CONFIG_SET_DEFAULT_CONTAINER"
24
+ PROJECT_CONFIG_GET = "PROJECT_CONFIG_GET"
25
+ CONTAINER_LS = "CONTAINER_LS"
26
+ CONTAINER_INFO = "CONTAINER_INFO"
27
+ CONTAINER_CONNECT = "CONTAINER_CONNECT"
28
+ CONTAINER_UPLOAD = "CONTAINER_UPLOAD"
29
+ CONTAINER_DOWNLOAD = "CONTAINER_DOWNLOAD"
30
+ CONTAINER_START = "CONTAINER_START"
31
+ CONTAINER_STOP = "CONTAINER_STOP"
32
+ CONTAINER_RESTART = "CONTAINER_RESTART"
33
+ CONTAINER_LOGS = "CONTAINER_LOGS"
34
+ INSTANCE_RENTED_LS = "INSTANCE_RENTED_LS"
35
+ INSTANCE_RENTED_CONNECT = "INSTANCE_RENTED_CONNECT"
36
+ INSTANCE_SELF_HOSTED_LS = "INSTANCE_SELF_HOSTED_LS"
37
+ INSTANCE_SELF_HOSTED_CONNECT = "INSTANCE_SELF_HOSTED_CONNECT"
38
+ CONFIG_GET = "CONFIG_GET"
39
+ CONFIG_SET = "CONFIG_SET"
40
+ CONFIG_CLEAR = "CONFIG_CLEAR"
41
+ CONFIG_UPLOAD_SSH_KEY = "CONFIG_UPLOAD_SSH_KEY"
42
+
43
+
44
+ class CliCommandAvailability(str, Enum):
45
+ ALLOWED = "ALLOWED"
46
+ RESTRICTED = "RESTRICTED"
47
+ DEPRECATED = "DEPRECATED"
48
+
49
+
50
+ ALWAYS_AVAILABLE_COMMANDS = [
51
+ CliCommand.VERSION,
52
+ CliCommand.PROJECT_CONFIG_GET,
53
+ CliCommand.CONFIG_GET,
54
+ CliCommand.CONFIG_SET,
55
+ CliCommand.CONFIG_CLEAR,
56
+ ]
@@ -0,0 +1,51 @@
1
+ from typing import Callable
2
+
3
+ import typer
4
+
5
+ from thestage.cli_command import CliCommand, CliCommandAvailability
6
+ from thestage.color_scheme.color_scheme import ColorScheme
7
+ from thestage.config import config_storage
8
+ from rich import print
9
+
10
+
11
+ def cli_command(command_id: CliCommand):
12
+ def decorator(func: Callable):
13
+ setattr(func, "__cli_command__", command_id)
14
+ return func
15
+ return decorator
16
+
17
+
18
+ def get_command_metadata(command_id: CliCommand) -> dict:
19
+ return {
20
+ "rich_help_panel": get_command_help_panel(command_id),
21
+ "deprecated": is_command_deprecated(command_id),
22
+ }
23
+
24
+
25
+ def get_command_group_help_panel() -> str:
26
+ return "Command Groups"
27
+
28
+
29
+ def get_command_help_panel(command: CliCommand) -> str:
30
+ if config_storage.APP_CONFIG.runtime.allowed_commands.get(command) == CliCommandAvailability.ALLOWED:
31
+ return "Allowed Commands"
32
+ if config_storage.APP_CONFIG.runtime.allowed_commands.get(command) == CliCommandAvailability.RESTRICTED:
33
+ return "Restricted Commands"
34
+ if config_storage.APP_CONFIG.runtime.allowed_commands.get(command) == CliCommandAvailability.DEPRECATED:
35
+ return "Deprecated Commands"
36
+
37
+
38
+ def is_command_deprecated(command: CliCommand) -> bool:
39
+ if config_storage.APP_CONFIG.runtime.allowed_commands.get(command) == CliCommandAvailability.DEPRECATED:
40
+ return True
41
+ return False
42
+
43
+
44
+ def check_command_permission(executed_command: CliCommand):
45
+ if config_storage.APP_CONFIG.runtime.is_token_valid == False and executed_command != CliCommand.CONFIG_SET:
46
+ print(f"[{ColorScheme.WARNING.value}]Your API Token is not valid. You can update the token using 'thestage config set' command[{ColorScheme.WARNING.value}]")
47
+
48
+ is_allowed = config_storage.APP_CONFIG.runtime.allowed_commands.get(executed_command) == CliCommandAvailability.ALLOWED
49
+ if not is_allowed:
50
+ typer.echo("Action is not allowed")
51
+ raise typer.Exit(code=1)
@@ -0,0 +1,3 @@
1
+ from thestage.services.core_files.config_entity import ConfigEntity
2
+
3
+ APP_CONFIG: ConfigEntity | None = None
@@ -1,6 +1,8 @@
1
1
  from pathlib import Path
2
2
  from typing import Optional
3
3
 
4
+ from thestage.cli_command import CliCommand
5
+ from thestage.cli_command_helper import get_command_metadata, check_command_permission
4
6
  from thestage.i18n.translation import __
5
7
  from thestage.helpers.logger.app_logger import app_logger
6
8
  from thestage.controllers.utils_controller import get_current_directory, validate_config_and_get_service_factory
@@ -10,24 +12,23 @@ import typer
10
12
 
11
13
  from thestage.services.connect.connect_service import ConnectService
12
14
 
13
- app = typer.Typer(no_args_is_help=True)
15
+ app = typer.Typer(no_args_is_help=True,)
14
16
 
15
17
 
16
- @app.command(no_args_is_help=False)
18
+ @app.command(name='version', help="Get application's name and version", no_args_is_help=False, **get_command_metadata(CliCommand.VERSION))
17
19
  def version():
18
- """
19
- Returns the application's name and version
20
- """
21
- app_logger.info(f'Start version from {get_current_directory()}')
22
- typer.echo(
23
- __("%app_name% v%version%", {'app_name': __app_name__, 'version': __version__}))
20
+ command_name = CliCommand.VERSION
21
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
22
+ check_command_permission(command_name)
23
+
24
+ typer.echo(f"{__app_name__} v{__version__}")
24
25
  raise typer.Exit(0)
25
26
 
26
27
 
27
- @app.command(name="connect", no_args_is_help=True, help=__("Connect to server instance or container using unique ID"))
28
+ @app.command(name="connect", no_args_is_help=True, help=__("Connect to server instance or container or task"), **get_command_metadata(CliCommand.CONNECT))
28
29
  def connect(
29
30
  uid: Optional[str] = typer.Argument(
30
- help=__("Unique ID of server instance or container"), ),
31
+ help=__("Unique ID of server instance or container or task ID"), ),
31
32
  username: Optional[str] = typer.Option(
32
33
  None,
33
34
  '--username',
@@ -43,10 +44,9 @@ def connect(
43
44
  is_eager=False,
44
45
  ),
45
46
  ):
46
- """
47
- Connects to entity with a unique ID
48
- """
49
- app_logger.info(f'Connect to some entity with UID')
47
+ command_name = CliCommand.CONNECT
48
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
49
+ check_command_permission(command_name)
50
50
 
51
51
  if private_ssh_key_path and not Path(private_ssh_key_path).is_file():
52
52
  typer.echo(f'No file found at provided path {private_ssh_key_path}')
@@ -56,7 +56,11 @@ def connect(
56
56
 
57
57
  connect_service: ConnectService = service_factory.get_connect_service()
58
58
 
59
- connect_service.connect_to_entity(uid=uid, username=username, private_key_path=private_ssh_key_path)
59
+ connect_service.connect_to_entity(
60
+ uid=uid,
61
+ username=username,
62
+ private_key_path=private_ssh_key_path
63
+ )
60
64
 
61
65
 
62
66
  app_logger.info(f'Stop connect to entity')
@@ -1,38 +1,37 @@
1
- import os
2
1
  from pathlib import Path
3
2
 
4
3
  import click
4
+
5
+ from thestage.cli_command import CliCommand
6
+ from thestage.cli_command_helper import get_command_metadata, check_command_permission
5
7
  from thestage.services.core_files.config_entity import ConfigEntity
6
8
 
7
9
  from thestage.entities.enums.yes_no_response import YesOrNoResponse
8
10
  from thestage.i18n.translation import __
9
11
  from thestage.helpers.logger.app_logger import app_logger, get_log_path_from_os
10
- from thestage.services.config_provider.config_provider import ConfigProvider
11
12
  from thestage.services.connect.connect_service import ConnectService
12
13
  from thestage.services.service_factory import ServiceFactory
13
- from thestage.controllers.utils_controller import get_current_directory, validate_config_and_get_service_factory
14
+ from thestage.controllers.utils_controller import get_current_directory
14
15
 
15
16
  import typer
16
17
 
17
18
  app = typer.Typer(no_args_is_help=True, help=__("Manage configuration settings"))
18
19
 
19
20
 
20
- @app.command(name='get', no_args_is_help=False, help=__("Display all configuration settings"))
21
+ @app.command(name='get', no_args_is_help=False, help=__("Display all configuration settings"), **get_command_metadata(CliCommand.CONFIG_GET))
21
22
  def config_get():
22
- """
23
- Lists all configuration settings
24
- """
25
- app_logger.info(f'Start config from {get_current_directory()}')
23
+ command_name = CliCommand.CONFIG_GET
24
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
25
+ check_command_permission(command_name)
26
26
 
27
- local_path = get_current_directory()
28
- config_provider = ConfigProvider(local_path=local_path)
29
- config: ConfigEntity = config_provider.get_full_config()
27
+ config_provider = ServiceFactory().get_config_provider()
28
+ config: ConfigEntity = config_provider.get_config()
30
29
 
31
30
  if not config:
32
31
  typer.echo(__('No configuration found'))
33
32
  raise typer.Exit(1)
34
33
 
35
- config_provider.save_config(config=config)
34
+ config_provider.save_config()
36
35
 
37
36
  typer.echo(__('THESTAGE TOKEN: %token%', {'token': config.main.thestage_auth_token or ''}))
38
37
  typer.echo(__('THESTAGE API LINK: %link%', {'link': config.main.thestage_api_url or ''}))
@@ -45,7 +44,7 @@ def config_get():
45
44
  raise typer.Exit(0)
46
45
 
47
46
 
48
- @app.command(name='set', no_args_is_help=True, help=__("Update configuration settings"))
47
+ @app.command(name='set', no_args_is_help=True, help=__("Update configuration settings"), **get_command_metadata(CliCommand.CONFIG_SET))
49
48
  def config_set(
50
49
  token: str = typer.Option(
51
50
  None,
@@ -55,40 +54,36 @@ def config_set(
55
54
  is_eager=False,
56
55
  ),
57
56
  ):
58
- """
59
- Updates configuration settings
60
- """
61
- app_logger.info(f'Start config from {get_current_directory()}')
62
-
63
- config_provider = ConfigProvider(local_path=get_current_directory())
64
- service_factory = ServiceFactory(config_provider)
57
+ command_name = CliCommand.CONFIG_SET
58
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
59
+ check_command_permission(command_name)
65
60
 
61
+ service_factory = ServiceFactory()
66
62
  app_service = service_factory.get_app_config_service()
67
63
 
68
64
  if token:
69
- app_service.app_change_token(config=config_provider.get_full_config(), token=token)
65
+ app_service.app_change_token(token=token)
70
66
 
71
67
  typer.echo('Configuration updated successfully')
72
68
  raise typer.Exit(0)
73
69
 
74
70
 
75
- @app.command(name='clear', no_args_is_help=False, help=__("Clear configuration"))
71
+ @app.command(name='clear', no_args_is_help=False, help=__("Clear configuration"), **get_command_metadata(CliCommand.CONFIG_CLEAR))
76
72
  def config_clear():
77
- """
78
- Clears all configuration settings
79
- """
80
- app_logger.info(f'Start config from {get_current_directory()}')
81
- local_path = get_current_directory()
82
- config_provider = ConfigProvider(local_path=local_path)
83
- config_dir = config_provider.get_full_config().runtime.config_global_path
73
+ command_name = CliCommand.CONFIG_CLEAR
74
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
75
+ check_command_permission(command_name)
76
+
77
+ config_provider = ServiceFactory().get_config_provider()
78
+ config_dir = config_provider.get_config().runtime.config_global_path
84
79
  config_provider.clear_config()
85
80
  typer.echo(f'Removed {config_dir}')
86
81
 
87
82
  raise typer.Exit(0)
88
83
 
89
84
 
90
- @app.command(name='upload-ssh-key', no_args_is_help=True, help=__("Send your public SSH key to the platform and / or rented server instance"))
91
- def config_set(
85
+ @app.command(name='upload-ssh-key', no_args_is_help=True, help=__("Send your public SSH key to the platform and / or rented server instance"), **get_command_metadata(CliCommand.CONFIG_UPLOAD_SSH_KEY))
86
+ def upload_ssh_key(
92
87
  ssh_public_key: str = typer.Argument(
93
88
  help=__("Path to your public SSH key file or your public SSH key contents"),
94
89
  ),
@@ -100,14 +95,11 @@ def config_set(
100
95
  is_eager=False,
101
96
  )
102
97
  ):
103
- """
104
- Sends SSH key to the platform
105
- """
106
-
107
- app_logger.info(f'Start config from {get_current_directory()}')
98
+ command_name = CliCommand.CONFIG_UPLOAD_SSH_KEY
99
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
100
+ check_command_permission(command_name)
108
101
 
109
- config_provider = ConfigProvider(local_path=get_current_directory())
110
- service_factory = ServiceFactory(config_provider)
102
+ service_factory = ServiceFactory()
111
103
  connect_service: ConnectService = service_factory.get_connect_service()
112
104
 
113
105
  is_path_provided_confirmed = False
@@ -3,6 +3,8 @@ import re
3
3
  from pathlib import Path
4
4
  from typing import Optional, List
5
5
 
6
+ from thestage.cli_command import CliCommand
7
+ from thestage.cli_command_helper import get_command_metadata, check_command_permission
6
8
  from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
7
9
  from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
8
10
  from thestage.i18n.translation import __
@@ -17,8 +19,8 @@ from thestage.services.logging.logging_service import LoggingService
17
19
  app = typer.Typer(no_args_is_help=True, help=__("Manage containers"))
18
20
 
19
21
 
20
- @app.command(name='ls', help=__("List containers"))
21
- def list_items(
22
+ @app.command(name='ls', help=__("List containers"), **get_command_metadata(CliCommand.CONTAINER_LS))
23
+ def list_containers(
22
24
  row: int = typer.Option(
23
25
  5,
24
26
  '--row',
@@ -48,18 +50,13 @@ def list_items(
48
50
  is_eager=False,
49
51
  ),
50
52
  ):
51
- """
52
- Lists containers
53
- """
54
- app_logger.info(f'Start container lists from {get_current_directory()}')
53
+ command_name = CliCommand.CONTAINER_LS
54
+ app_logger.info(f'Running {command_name}')
55
+ check_command_permission(command_name)
55
56
 
56
57
  service_factory = validate_config_and_get_service_factory()
57
- config = service_factory.get_config_provider().get_full_config()
58
-
59
58
  container_service: ContainerService = service_factory.get_container_service()
60
-
61
59
  container_service.print_container_list(
62
- config=config,
63
60
  row=row,
64
61
  page=page,
65
62
  project_uid=project_uid,
@@ -70,26 +67,22 @@ def list_items(
70
67
  raise typer.Exit(0)
71
68
 
72
69
 
73
- @app.command(name="info", no_args_is_help=True, help=__("Help get container details"))
74
- def item_details(
70
+ @app.command(name="info", no_args_is_help=True, help=__("Get container info"), **get_command_metadata(CliCommand.CONTAINER_INFO))
71
+ def container_info(
75
72
  container_uid: Optional[str] = typer.Argument(hidden=False, help=__("Container unique ID")),
76
73
  ):
77
- """
78
- Lists container details
79
- """
80
- app_logger.info(f'Start container details')
74
+ command_name = CliCommand.CONTAINER_INFO
75
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
76
+ check_command_permission(command_name)
81
77
 
82
78
  if not container_uid:
83
79
  typer.echo(__('Container unique ID is required'))
84
80
  raise typer.Exit(1)
85
81
 
86
82
  service_factory = validate_config_and_get_service_factory()
87
- config = service_factory.get_config_provider().get_full_config()
88
-
89
83
  container_service: ContainerService = service_factory.get_container_service()
90
84
 
91
85
  container: Optional[DockerContainerDto] = container_service.get_container(
92
- config=config,
93
86
  container_slug=container_uid,
94
87
  )
95
88
 
@@ -134,7 +127,7 @@ def item_details(
134
127
  raise typer.Exit(0)
135
128
 
136
129
 
137
- @app.command(name="connect", no_args_is_help=True, help=__("Connect to container"))
130
+ @app.command(name="connect", no_args_is_help=True, help=__("Connect to container"), **get_command_metadata(CliCommand.CONTAINER_CONNECT))
138
131
  def container_connect(
139
132
  container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"),),
140
133
  username: Optional[str] = typer.Option(
@@ -152,10 +145,9 @@ def container_connect(
152
145
  is_eager=False,
153
146
  ),
154
147
  ):
155
- """
156
- Connects to container
157
- """
158
- app_logger.info(f'Connect to container')
148
+ command_name = CliCommand.CONTAINER_CONNECT
149
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
150
+ check_command_permission(command_name)
159
151
 
160
152
  if not container_uid:
161
153
  typer.echo(__('Container unique ID is required'))
@@ -166,12 +158,9 @@ def container_connect(
166
158
  raise typer.Exit(1)
167
159
 
168
160
  service_factory = validate_config_and_get_service_factory()
169
- config = service_factory.get_config_provider().get_full_config()
170
-
171
161
  container_service: ContainerService = service_factory.get_container_service()
172
162
 
173
163
  container_service.connect_to_container(
174
- config=config,
175
164
  container_uid=container_uid,
176
165
  username=username,
177
166
  input_ssh_key_path=private_ssh_key_path,
@@ -181,8 +170,8 @@ def container_connect(
181
170
  raise typer.Exit(0)
182
171
 
183
172
 
184
- @app.command(name="upload", no_args_is_help=True, help=__("Upload file to container"))
185
- def put_file(
173
+ @app.command(name="upload", no_args_is_help=True, help=__("Upload file to container"), **get_command_metadata(CliCommand.CONTAINER_UPLOAD))
174
+ def upload_file(
186
175
  source_path: str = typer.Argument(help=__("Source file path"),),
187
176
  destination: Optional[str] = typer.Argument(help=__("Destination directory path in container. Format: container_uid:/path/to/file"),),
188
177
  username: Optional[str] = typer.Option(
@@ -193,10 +182,9 @@ def put_file(
193
182
  is_eager=False,
194
183
  ),
195
184
  ):
196
- """
197
- Uploads file to container
198
- """
199
- app_logger.info(f'Push file to container')
185
+ command_name = CliCommand.CONTAINER_UPLOAD
186
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
187
+ check_command_permission(command_name)
200
188
 
201
189
  container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", destination)
202
190
 
@@ -212,12 +200,9 @@ def put_file(
212
200
  raise typer.Exit(1)
213
201
 
214
202
  service_factory = validate_config_and_get_service_factory()
215
- config = service_factory.get_config_provider().get_full_config()
216
-
217
203
  container_service: ContainerService = service_factory.get_container_service()
218
204
 
219
205
  container: Optional[DockerContainerDto] = container_service.get_container(
220
- config=config,
221
206
  container_slug=container_slug,
222
207
  )
223
208
 
@@ -242,7 +227,7 @@ def put_file(
242
227
  raise typer.Exit(0)
243
228
 
244
229
 
245
- @app.command(name="download", no_args_is_help=True, help=__("Download file from container"))
230
+ @app.command(name="download", no_args_is_help=True, help=__("Download file from container"), **get_command_metadata(CliCommand.CONTAINER_DOWNLOAD))
246
231
  def download_file(
247
232
  source_path: str = typer.Argument(help=__("Source file path in container. Format: container_uid:/path/to/file"),),
248
233
  destination_path: str = typer.Argument(help=__("Destination directory path on local machine"),),
@@ -254,10 +239,9 @@ def download_file(
254
239
  is_eager=False,
255
240
  ),
256
241
  ):
257
- """
258
- Downloads file from container
259
- """
260
- app_logger.info(f'Download file from container')
242
+ command_name = CliCommand.CONTAINER_DOWNLOAD
243
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
244
+ check_command_permission(command_name)
261
245
 
262
246
  container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", source_path)
263
247
 
@@ -273,12 +257,9 @@ def download_file(
273
257
  raise typer.Exit(1)
274
258
 
275
259
  service_factory = validate_config_and_get_service_factory()
276
- config = service_factory.get_config_provider().get_full_config()
277
-
278
260
  container_service: ContainerService = service_factory.get_container_service()
279
261
 
280
262
  container: Optional[DockerContainerDto] = container_service.get_container(
281
- config=config,
282
263
  container_slug=container_slug,
283
264
  )
284
265
 
@@ -295,7 +276,6 @@ def download_file(
295
276
  destination_path=destination_path.rstrip("/"),
296
277
  username_param=username,
297
278
  copy_only_folder_contents=source_path.endswith("/"),
298
- config=config
299
279
  )
300
280
  else:
301
281
  typer.echo(__("Container not found: %container_item%", {'container_item': container_slug}))
@@ -304,26 +284,22 @@ def download_file(
304
284
  raise typer.Exit(0)
305
285
 
306
286
 
307
- @app.command(name="start", no_args_is_help=True, help=__("Start container"))
287
+ @app.command(name="start", no_args_is_help=True, help=__("Start container"), **get_command_metadata(CliCommand.CONTAINER_START))
308
288
  def start_container(
309
289
  container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
310
290
  ):
311
- """
312
- Starts container
313
- """
314
- app_logger.info(f'Start container')
291
+ command_name = CliCommand.CONTAINER_START
292
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
293
+ check_command_permission(command_name)
315
294
 
316
295
  if not container_uid:
317
296
  typer.echo(__('Container unique ID is required'))
318
297
  raise typer.Exit(1)
319
298
 
320
299
  service_factory = validate_config_and_get_service_factory()
321
- config = service_factory.get_config_provider().get_full_config()
322
-
323
300
  container_service: ContainerService = service_factory.get_container_service()
324
301
 
325
302
  container_service.request_docker_container_action(
326
- config=config,
327
303
  container_uid=container_uid,
328
304
  action=DockerContainerAction.START
329
305
  )
@@ -332,26 +308,22 @@ def start_container(
332
308
  raise typer.Exit(0)
333
309
 
334
310
 
335
- @app.command(name="stop", no_args_is_help=True, help=__("Stop container"))
311
+ @app.command(name="stop", no_args_is_help=True, help=__("Stop container"), **get_command_metadata(CliCommand.CONTAINER_STOP))
336
312
  def stop_container(
337
313
  container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
338
314
  ):
339
- """
340
- Stops container
341
- """
342
- app_logger.info(f'Stop container')
315
+ command_name = CliCommand.CONTAINER_STOP
316
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
317
+ check_command_permission(command_name)
343
318
 
344
319
  if not container_uid:
345
320
  typer.echo(__('Container unique ID is required'))
346
321
  raise typer.Exit(1)
347
322
 
348
323
  service_factory = validate_config_and_get_service_factory()
349
- config = service_factory.get_config_provider().get_full_config()
350
-
351
324
  container_service: ContainerService = service_factory.get_container_service()
352
325
 
353
326
  container_service.request_docker_container_action(
354
- config=config,
355
327
  container_uid=container_uid,
356
328
  action=DockerContainerAction.STOP
357
329
  )
@@ -360,26 +332,22 @@ def stop_container(
360
332
  raise typer.Exit(0)
361
333
 
362
334
 
363
- @app.command(name="restart", no_args_is_help=True, help=__("Restart container"))
364
- def stop_container(
335
+ @app.command(name="restart", no_args_is_help=True, help=__("Restart container"), **get_command_metadata(CliCommand.CONTAINER_RESTART))
336
+ def restart_container(
365
337
  container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
366
338
  ):
367
- """
368
- Stops container
369
- """
370
- app_logger.info(f'Restart container')
339
+ command_name = CliCommand.CONTAINER_RESTART
340
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
341
+ check_command_permission(command_name)
371
342
 
372
343
  if not container_uid:
373
344
  typer.echo(__('Container unique ID is required'))
374
345
  raise typer.Exit(1)
375
346
 
376
347
  service_factory = validate_config_and_get_service_factory()
377
- config = service_factory.get_config_provider().get_full_config()
378
-
379
348
  container_service: ContainerService = service_factory.get_container_service()
380
349
 
381
350
  container_service.request_docker_container_action(
382
- config=config,
383
351
  container_uid=container_uid,
384
352
  action=DockerContainerAction.RESTART
385
353
  )
@@ -388,7 +356,7 @@ def stop_container(
388
356
  raise typer.Exit(0)
389
357
 
390
358
 
391
- @app.command(name="logs", no_args_is_help=True, help=__("Stream real-time Docker container logs or view last logs for a container"))
359
+ @app.command(name="logs", no_args_is_help=True, help=__("Stream real-time Docker container logs or view last logs for a container"), **get_command_metadata(CliCommand.CONTAINER_LOGS))
392
360
  def container_logs(
393
361
  container_uid: Optional[str] = typer.Argument(help=__("Container unique id")),
394
362
  logs_number: Optional[int] = typer.Option(
@@ -399,27 +367,23 @@ def container_logs(
399
367
  is_eager=False,
400
368
  ),
401
369
  ):
402
- """
403
- Streams real-time container logs
404
- """
405
- app_logger.info(f'View container logs')
370
+ command_name = CliCommand.CONTAINER_LOGS
371
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
372
+ check_command_permission(command_name)
406
373
 
407
374
  if not container_uid:
408
375
  typer.echo(__('Container unique ID is required'))
409
376
  raise typer.Exit(1)
410
377
 
411
378
  service_factory = validate_config_and_get_service_factory()
412
- config = service_factory.get_config_provider().get_full_config()
413
-
414
379
  logging_service: LoggingService = service_factory.get_logging_service()
415
380
 
416
381
  if logs_number is None:
417
382
  logging_service.stream_container_logs_with_controls(
418
- config=config,
419
383
  container_uid=container_uid
420
384
  )
421
385
  else:
422
- logging_service.print_last_container_logs(config=config, container_uid=container_uid, logs_number=logs_number)
386
+ logging_service.print_last_container_logs(container_uid=container_uid, logs_number=logs_number)
423
387
 
424
388
  app_logger.info(f'Container logs - end')
425
389
  raise typer.Exit(0)