dstack 0.19.25__py3-none-any.whl → 0.19.26__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.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/__init__.py +2 -2
- dstack/_internal/cli/commands/apply.py +3 -61
- dstack/_internal/cli/commands/attach.py +1 -1
- dstack/_internal/cli/commands/completion.py +1 -1
- dstack/_internal/cli/commands/delete.py +2 -2
- dstack/_internal/cli/commands/fleet.py +1 -1
- dstack/_internal/cli/commands/gateway.py +2 -2
- dstack/_internal/cli/commands/init.py +56 -24
- dstack/_internal/cli/commands/logs.py +1 -1
- dstack/_internal/cli/commands/metrics.py +1 -1
- dstack/_internal/cli/commands/offer.py +45 -7
- dstack/_internal/cli/commands/project.py +2 -2
- dstack/_internal/cli/commands/secrets.py +2 -2
- dstack/_internal/cli/commands/server.py +1 -1
- dstack/_internal/cli/commands/stop.py +1 -1
- dstack/_internal/cli/commands/volume.py +1 -1
- dstack/_internal/cli/main.py +2 -2
- dstack/_internal/cli/services/completion.py +2 -2
- dstack/_internal/cli/services/configurators/__init__.py +6 -2
- dstack/_internal/cli/services/configurators/base.py +6 -7
- dstack/_internal/cli/services/configurators/fleet.py +1 -3
- dstack/_internal/cli/services/configurators/gateway.py +2 -4
- dstack/_internal/cli/services/configurators/run.py +195 -55
- dstack/_internal/cli/services/configurators/volume.py +2 -4
- dstack/_internal/cli/services/profile.py +1 -1
- dstack/_internal/cli/services/repos.py +51 -47
- dstack/_internal/core/backends/aws/configurator.py +11 -7
- dstack/_internal/core/backends/azure/configurator.py +11 -7
- dstack/_internal/core/backends/base/configurator.py +25 -13
- dstack/_internal/core/backends/cloudrift/configurator.py +13 -7
- dstack/_internal/core/backends/cudo/configurator.py +11 -7
- dstack/_internal/core/backends/datacrunch/compute.py +5 -1
- dstack/_internal/core/backends/datacrunch/configurator.py +13 -7
- dstack/_internal/core/backends/gcp/configurator.py +11 -7
- dstack/_internal/core/backends/hotaisle/configurator.py +13 -7
- dstack/_internal/core/backends/kubernetes/configurator.py +13 -7
- dstack/_internal/core/backends/lambdalabs/configurator.py +11 -7
- dstack/_internal/core/backends/nebius/compute.py +1 -1
- dstack/_internal/core/backends/nebius/configurator.py +11 -7
- dstack/_internal/core/backends/nebius/resources.py +21 -11
- dstack/_internal/core/backends/oci/configurator.py +11 -7
- dstack/_internal/core/backends/runpod/configurator.py +11 -7
- dstack/_internal/core/backends/template/configurator.py.jinja +11 -7
- dstack/_internal/core/backends/tensordock/configurator.py +13 -7
- dstack/_internal/core/backends/vastai/configurator.py +11 -7
- dstack/_internal/core/backends/vultr/configurator.py +11 -4
- dstack/_internal/core/compatibility/gpus.py +13 -0
- dstack/_internal/core/compatibility/runs.py +1 -0
- dstack/_internal/core/models/common.py +3 -3
- dstack/_internal/core/models/configurations.py +172 -27
- dstack/_internal/core/models/files.py +1 -1
- dstack/_internal/core/models/fleets.py +5 -1
- dstack/_internal/core/models/profiles.py +41 -11
- dstack/_internal/core/models/resources.py +46 -42
- dstack/_internal/core/models/runs.py +4 -0
- dstack/_internal/core/services/configs/__init__.py +2 -2
- dstack/_internal/core/services/profiles.py +2 -2
- dstack/_internal/core/services/repos.py +5 -3
- dstack/_internal/core/services/ssh/ports.py +1 -1
- dstack/_internal/proxy/lib/deps.py +6 -2
- dstack/_internal/server/app.py +22 -17
- dstack/_internal/server/background/tasks/process_gateways.py +4 -1
- dstack/_internal/server/background/tasks/process_instances.py +10 -2
- dstack/_internal/server/background/tasks/process_probes.py +1 -1
- dstack/_internal/server/background/tasks/process_running_jobs.py +10 -4
- dstack/_internal/server/background/tasks/process_runs.py +1 -1
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +54 -43
- dstack/_internal/server/background/tasks/process_terminating_jobs.py +2 -2
- dstack/_internal/server/background/tasks/process_volumes.py +1 -1
- dstack/_internal/server/db.py +8 -4
- dstack/_internal/server/models.py +1 -0
- dstack/_internal/server/routers/gpus.py +1 -6
- dstack/_internal/server/schemas/runner.py +10 -0
- dstack/_internal/server/services/backends/__init__.py +14 -8
- dstack/_internal/server/services/backends/handlers.py +6 -1
- dstack/_internal/server/services/docker.py +5 -5
- dstack/_internal/server/services/fleets.py +14 -13
- dstack/_internal/server/services/gateways/__init__.py +2 -0
- dstack/_internal/server/services/gateways/client.py +5 -2
- dstack/_internal/server/services/gateways/connection.py +1 -1
- dstack/_internal/server/services/gpus.py +50 -49
- dstack/_internal/server/services/instances.py +41 -1
- dstack/_internal/server/services/jobs/__init__.py +15 -4
- dstack/_internal/server/services/jobs/configurators/base.py +7 -11
- dstack/_internal/server/services/jobs/configurators/dev.py +5 -0
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +3 -3
- dstack/_internal/server/services/jobs/configurators/extensions/vscode.py +3 -3
- dstack/_internal/server/services/jobs/configurators/service.py +1 -0
- dstack/_internal/server/services/jobs/configurators/task.py +3 -0
- dstack/_internal/server/services/locking.py +5 -5
- dstack/_internal/server/services/logging.py +10 -2
- dstack/_internal/server/services/logs/__init__.py +8 -6
- dstack/_internal/server/services/logs/aws.py +330 -327
- dstack/_internal/server/services/logs/filelog.py +7 -6
- dstack/_internal/server/services/logs/gcp.py +141 -139
- dstack/_internal/server/services/plugins.py +1 -1
- dstack/_internal/server/services/projects.py +2 -5
- dstack/_internal/server/services/proxy/repo.py +5 -1
- dstack/_internal/server/services/requirements/__init__.py +0 -0
- dstack/_internal/server/services/requirements/combine.py +259 -0
- dstack/_internal/server/services/runner/client.py +7 -0
- dstack/_internal/server/services/runs.py +1 -1
- dstack/_internal/server/services/services/__init__.py +8 -2
- dstack/_internal/server/services/services/autoscalers.py +2 -0
- dstack/_internal/server/services/ssh.py +2 -1
- dstack/_internal/server/services/storage/__init__.py +5 -6
- dstack/_internal/server/services/storage/gcs.py +49 -49
- dstack/_internal/server/services/storage/s3.py +52 -52
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/testing/common.py +1 -1
- dstack/_internal/server/utils/logging.py +3 -3
- dstack/_internal/server/utils/provisioning.py +3 -3
- dstack/_internal/utils/json_schema.py +3 -1
- dstack/_internal/utils/typing.py +14 -0
- dstack/api/_public/repos.py +21 -2
- dstack/api/_public/runs.py +5 -7
- dstack/api/server/__init__.py +17 -19
- dstack/api/server/_gpus.py +2 -1
- dstack/api/server/_group.py +4 -3
- dstack/api/server/_repos.py +20 -3
- dstack/plugins/builtin/rest_plugin/_plugin.py +1 -0
- dstack/version.py +1 -1
- {dstack-0.19.25.dist-info → dstack-0.19.26.dist-info}/METADATA +1 -1
- {dstack-0.19.25.dist-info → dstack-0.19.26.dist-info}/RECORD +127 -124
- dstack/api/huggingface/__init__.py +0 -73
- {dstack-0.19.25.dist-info → dstack-0.19.26.dist-info}/WHEEL +0 -0
- {dstack-0.19.25.dist-info → dstack-0.19.26.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.25.dist-info → dstack-0.19.26.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -54,7 +54,7 @@ class BaseCommand(ABC):
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class APIBaseCommand(BaseCommand):
|
|
57
|
-
api: Client
|
|
57
|
+
api: Client
|
|
58
58
|
|
|
59
59
|
def _register(self):
|
|
60
60
|
self._parser.add_argument(
|
|
@@ -62,7 +62,7 @@ class APIBaseCommand(BaseCommand):
|
|
|
62
62
|
help="The name of the project. Defaults to [code]$DSTACK_PROJECT[/]",
|
|
63
63
|
metavar="NAME",
|
|
64
64
|
default=os.getenv("DSTACK_PROJECT"),
|
|
65
|
-
).completer = ProjectNameCompleter()
|
|
65
|
+
).completer = ProjectNameCompleter() # type: ignore[attr-defined]
|
|
66
66
|
|
|
67
67
|
def _command(self, args: argparse.Namespace):
|
|
68
68
|
configure_logging()
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
from pathlib import Path
|
|
3
2
|
|
|
4
|
-
from argcomplete import FilesCompleter
|
|
3
|
+
from argcomplete import FilesCompleter # type: ignore[attr-defined]
|
|
5
4
|
|
|
6
5
|
from dstack._internal.cli.commands import APIBaseCommand
|
|
7
6
|
from dstack._internal.cli.services.configurators import (
|
|
@@ -9,12 +8,7 @@ from dstack._internal.cli.services.configurators import (
|
|
|
9
8
|
get_apply_configurator_class,
|
|
10
9
|
load_apply_configuration,
|
|
11
10
|
)
|
|
12
|
-
from dstack._internal.cli.
|
|
13
|
-
init_default_virtual_repo,
|
|
14
|
-
init_repo,
|
|
15
|
-
register_init_repo_args,
|
|
16
|
-
)
|
|
17
|
-
from dstack._internal.cli.utils.common import console, warn
|
|
11
|
+
from dstack._internal.cli.utils.common import console
|
|
18
12
|
from dstack._internal.core.errors import CLIError
|
|
19
13
|
from dstack._internal.core.models.configurations import ApplyConfigurationType
|
|
20
14
|
|
|
@@ -48,7 +42,7 @@ class ApplyCommand(APIBaseCommand):
|
|
|
48
42
|
" Defaults to [code]$PWD/.dstack.yml[/]"
|
|
49
43
|
),
|
|
50
44
|
dest="configuration_file",
|
|
51
|
-
).completer = FilesCompleter(allowednames=["*.yml", "*.yaml"])
|
|
45
|
+
).completer = FilesCompleter(allowednames=["*.yml", "*.yaml"]) # type: ignore[attr-defined]
|
|
52
46
|
self._parser.add_argument(
|
|
53
47
|
"-y",
|
|
54
48
|
"--yes",
|
|
@@ -66,37 +60,6 @@ class ApplyCommand(APIBaseCommand):
|
|
|
66
60
|
help="Exit immediately after submitting configuration",
|
|
67
61
|
action="store_true",
|
|
68
62
|
)
|
|
69
|
-
self._parser.add_argument(
|
|
70
|
-
"--ssh-identity",
|
|
71
|
-
metavar="SSH_PRIVATE_KEY",
|
|
72
|
-
help="The private SSH key path for SSH tunneling",
|
|
73
|
-
type=Path,
|
|
74
|
-
dest="ssh_identity_file",
|
|
75
|
-
)
|
|
76
|
-
repo_group = self._parser.add_argument_group("Repo Options")
|
|
77
|
-
repo_group.add_argument(
|
|
78
|
-
"-P",
|
|
79
|
-
"--repo",
|
|
80
|
-
help=("The repo to use for the run. Can be a local path or a Git repo URL."),
|
|
81
|
-
dest="repo",
|
|
82
|
-
)
|
|
83
|
-
repo_group.add_argument(
|
|
84
|
-
"--repo-branch",
|
|
85
|
-
help="The repo branch to use for the run",
|
|
86
|
-
dest="repo_branch",
|
|
87
|
-
)
|
|
88
|
-
repo_group.add_argument(
|
|
89
|
-
"--repo-hash",
|
|
90
|
-
help="The hash of the repo commit to use for the run",
|
|
91
|
-
dest="repo_hash",
|
|
92
|
-
)
|
|
93
|
-
repo_group.add_argument(
|
|
94
|
-
"--no-repo",
|
|
95
|
-
help="Do not use any repo for the run",
|
|
96
|
-
dest="no_repo",
|
|
97
|
-
action="store_true",
|
|
98
|
-
)
|
|
99
|
-
register_init_repo_args(repo_group)
|
|
100
63
|
|
|
101
64
|
def _command(self, args: argparse.Namespace):
|
|
102
65
|
try:
|
|
@@ -117,26 +80,6 @@ class ApplyCommand(APIBaseCommand):
|
|
|
117
80
|
super()._command(args)
|
|
118
81
|
if not args.yes and args.configuration_file == APPLY_STDIN_NAME:
|
|
119
82
|
raise CLIError("Cannot read configuration from stdin if -y/--yes is not specified")
|
|
120
|
-
if args.repo and args.no_repo:
|
|
121
|
-
raise CLIError("Either --repo or --no-repo can be specified")
|
|
122
|
-
if args.local:
|
|
123
|
-
warn(
|
|
124
|
-
"Local repos are deprecated since 0.19.25 and will be removed soon."
|
|
125
|
-
" Consider using `files` instead: https://dstack.ai/docs/concepts/tasks/#files"
|
|
126
|
-
)
|
|
127
|
-
repo = None
|
|
128
|
-
if args.repo:
|
|
129
|
-
repo = init_repo(
|
|
130
|
-
api=self.api,
|
|
131
|
-
repo_path=args.repo,
|
|
132
|
-
repo_branch=args.repo_branch,
|
|
133
|
-
repo_hash=args.repo_hash,
|
|
134
|
-
local=args.local,
|
|
135
|
-
git_identity_file=args.git_identity_file,
|
|
136
|
-
oauth_token=args.gh_token,
|
|
137
|
-
)
|
|
138
|
-
elif args.no_repo:
|
|
139
|
-
repo = init_default_virtual_repo(api=self.api)
|
|
140
83
|
configuration_path, configuration = load_apply_configuration(args.configuration_file)
|
|
141
84
|
configurator_class = get_apply_configurator_class(configuration.type)
|
|
142
85
|
configurator = configurator_class(api_client=self.api)
|
|
@@ -148,7 +91,6 @@ class ApplyCommand(APIBaseCommand):
|
|
|
148
91
|
command_args=args,
|
|
149
92
|
configurator_args=known,
|
|
150
93
|
unknown_args=unknown,
|
|
151
|
-
repo=repo,
|
|
152
94
|
)
|
|
153
95
|
except KeyboardInterrupt:
|
|
154
96
|
console.print("\nOperation interrupted by user. Exiting...")
|
|
@@ -61,7 +61,7 @@ class AttachCommand(APIBaseCommand):
|
|
|
61
61
|
type=int,
|
|
62
62
|
default=0,
|
|
63
63
|
)
|
|
64
|
-
self._parser.add_argument("run_name").completer = RunNameCompleter()
|
|
64
|
+
self._parser.add_argument("run_name").completer = RunNameCompleter() # type: ignore[attr-defined]
|
|
65
65
|
|
|
66
66
|
def _command(self, args: argparse.Namespace):
|
|
67
67
|
super()._command(args)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
from argcomplete import FilesCompleter
|
|
4
|
+
from argcomplete import FilesCompleter # type: ignore[attr-defined]
|
|
5
5
|
|
|
6
6
|
from dstack._internal.cli.commands import APIBaseCommand
|
|
7
7
|
from dstack._internal.cli.services.configurators import (
|
|
@@ -24,7 +24,7 @@ class DeleteCommand(APIBaseCommand):
|
|
|
24
24
|
metavar="FILE",
|
|
25
25
|
help="The path to the configuration file. Defaults to [code]$PWD/.dstack.yml[/]",
|
|
26
26
|
dest="configuration_file",
|
|
27
|
-
).completer = FilesCompleter(allowednames=["*.yml", "*.yaml"])
|
|
27
|
+
).completer = FilesCompleter(allowednames=["*.yml", "*.yaml"]) # type: ignore[attr-defined]
|
|
28
28
|
self._parser.add_argument(
|
|
29
29
|
"-y",
|
|
30
30
|
"--yes",
|
|
@@ -48,7 +48,7 @@ class FleetCommand(APIBaseCommand):
|
|
|
48
48
|
delete_parser.add_argument(
|
|
49
49
|
"name",
|
|
50
50
|
help="The name of the fleet",
|
|
51
|
-
).completer = FleetNameCompleter()
|
|
51
|
+
).completer = FleetNameCompleter() # type: ignore[attr-defined]
|
|
52
52
|
delete_parser.add_argument(
|
|
53
53
|
"-i",
|
|
54
54
|
"--instance",
|
|
@@ -68,7 +68,7 @@ class GatewayCommand(APIBaseCommand):
|
|
|
68
68
|
delete_parser.set_defaults(subfunc=self._delete)
|
|
69
69
|
delete_parser.add_argument(
|
|
70
70
|
"name", help="The name of the gateway"
|
|
71
|
-
).completer = GatewayNameCompleter()
|
|
71
|
+
).completer = GatewayNameCompleter() # type: ignore[attr-defined]
|
|
72
72
|
delete_parser.add_argument(
|
|
73
73
|
"-y", "--yes", action="store_true", help="Don't ask for confirmation"
|
|
74
74
|
)
|
|
@@ -79,7 +79,7 @@ class GatewayCommand(APIBaseCommand):
|
|
|
79
79
|
update_parser.set_defaults(subfunc=self._update)
|
|
80
80
|
update_parser.add_argument(
|
|
81
81
|
"name", help="The name of the gateway"
|
|
82
|
-
).completer = GatewayNameCompleter()
|
|
82
|
+
).completer = GatewayNameCompleter() # type: ignore[attr-defined]
|
|
83
83
|
update_parser.add_argument(
|
|
84
84
|
"--set-default", action="store_true", help="Set it the default gateway for the project"
|
|
85
85
|
)
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
4
5
|
|
|
5
6
|
from dstack._internal.cli.commands import BaseCommand
|
|
6
|
-
from dstack._internal.cli.services.repos import
|
|
7
|
+
from dstack._internal.cli.services.repos import (
|
|
8
|
+
get_repo_from_dir,
|
|
9
|
+
get_repo_from_url,
|
|
10
|
+
is_git_repo_url,
|
|
11
|
+
register_init_repo_args,
|
|
12
|
+
)
|
|
7
13
|
from dstack._internal.cli.utils.common import configure_logging, confirm_ask, console, warn
|
|
8
14
|
from dstack._internal.core.errors import ConfigurationError
|
|
9
|
-
from dstack._internal.core.models.repos.base import RepoType
|
|
10
15
|
from dstack._internal.core.services.configs import ConfigManager
|
|
11
16
|
from dstack.api import Client
|
|
12
17
|
|
|
@@ -21,6 +26,15 @@ class InitCommand(BaseCommand):
|
|
|
21
26
|
help="The name of the project",
|
|
22
27
|
default=os.getenv("DSTACK_PROJECT"),
|
|
23
28
|
)
|
|
29
|
+
self._parser.add_argument(
|
|
30
|
+
"-P",
|
|
31
|
+
"--repo",
|
|
32
|
+
help=(
|
|
33
|
+
"The repo to initialize. Can be a local path or a Git repo URL."
|
|
34
|
+
" Defaults to the current working directory."
|
|
35
|
+
),
|
|
36
|
+
dest="repo",
|
|
37
|
+
)
|
|
24
38
|
register_init_repo_args(self._parser)
|
|
25
39
|
# Deprecated since 0.19.25, ignored
|
|
26
40
|
self._parser.add_argument(
|
|
@@ -30,7 +44,7 @@ class InitCommand(BaseCommand):
|
|
|
30
44
|
type=Path,
|
|
31
45
|
dest="ssh_identity_file",
|
|
32
46
|
)
|
|
33
|
-
# A hidden mode for transitional period only, remove it with
|
|
47
|
+
# A hidden mode for transitional period only, remove it with repos in `config.yml`
|
|
34
48
|
self._parser.add_argument(
|
|
35
49
|
"--remove",
|
|
36
50
|
help=argparse.SUPPRESS,
|
|
@@ -39,44 +53,62 @@ class InitCommand(BaseCommand):
|
|
|
39
53
|
|
|
40
54
|
def _command(self, args: argparse.Namespace):
|
|
41
55
|
configure_logging()
|
|
56
|
+
|
|
57
|
+
repo_path: Optional[Path] = None
|
|
58
|
+
repo_url: Optional[str] = None
|
|
59
|
+
repo_arg: Optional[str] = args.repo
|
|
60
|
+
if repo_arg is not None:
|
|
61
|
+
if is_git_repo_url(repo_arg):
|
|
62
|
+
repo_url = repo_arg
|
|
63
|
+
else:
|
|
64
|
+
repo_path = Path(repo_arg).expanduser().resolve()
|
|
65
|
+
else:
|
|
66
|
+
repo_path = Path.cwd()
|
|
67
|
+
|
|
42
68
|
if args.remove:
|
|
69
|
+
if repo_url is not None:
|
|
70
|
+
raise ConfigurationError(f"Local path expected, got URL: {repo_url}")
|
|
71
|
+
assert repo_path is not None
|
|
43
72
|
config_manager = ConfigManager()
|
|
44
|
-
repo_path = Path.cwd()
|
|
45
73
|
repo_config = config_manager.get_repo_config(repo_path)
|
|
46
74
|
if repo_config is None:
|
|
47
|
-
raise ConfigurationError("
|
|
48
|
-
if repo_config.repo_type != RepoType.LOCAL:
|
|
49
|
-
raise ConfigurationError("`dstack init --remove` is for local repos only")
|
|
75
|
+
raise ConfigurationError("Repo record not found, nothing to remove")
|
|
50
76
|
console.print(
|
|
51
|
-
f"You are about to remove the
|
|
77
|
+
f"You are about to remove the repo {repo_path}\n"
|
|
52
78
|
"Only the record about the repo will be removed,"
|
|
53
79
|
" the repo files will remain intact\n"
|
|
54
80
|
)
|
|
55
|
-
if not confirm_ask("Remove the
|
|
81
|
+
if not confirm_ask("Remove the repo?"):
|
|
56
82
|
return
|
|
57
83
|
config_manager.delete_repo_config(repo_config.repo_id)
|
|
58
84
|
config_manager.save()
|
|
59
|
-
console.print("
|
|
85
|
+
console.print("Repo has been removed")
|
|
60
86
|
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if args.local:
|
|
87
|
+
|
|
88
|
+
local: bool = args.local
|
|
89
|
+
if local:
|
|
65
90
|
warn(
|
|
66
|
-
"Local repos are deprecated since 0.19.25 and will be removed soon."
|
|
67
|
-
"
|
|
91
|
+
"Local repos are deprecated since 0.19.25 and will be removed soon. Consider"
|
|
92
|
+
" using [code]files[/code] instead: https://dstack.ai/docs/concepts/tasks/#files"
|
|
68
93
|
)
|
|
69
94
|
if args.ssh_identity_file:
|
|
70
95
|
warn(
|
|
71
|
-
"
|
|
72
|
-
" Use this option with
|
|
96
|
+
"[code]--ssh-identity[/code] in [code]dstack init[/code] is deprecated and ignored"
|
|
97
|
+
" since 0.19.25. Use this option with [code]dstack apply[/code]"
|
|
98
|
+
" and [code]dstack attach[/code] instead"
|
|
73
99
|
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
100
|
+
|
|
101
|
+
if repo_url is not None:
|
|
102
|
+
# Dummy repo branch to avoid autodetection that fails on private repos.
|
|
103
|
+
# We don't need branch/hash for repo_id anyway.
|
|
104
|
+
repo = get_repo_from_url(repo_url, repo_branch="master")
|
|
105
|
+
elif repo_path is not None:
|
|
106
|
+
repo = get_repo_from_dir(repo_path, local=local)
|
|
107
|
+
else:
|
|
108
|
+
assert False, "should not reach here"
|
|
109
|
+
api = Client.from_config(project_name=args.project)
|
|
110
|
+
api.repos.init(
|
|
111
|
+
repo=repo,
|
|
80
112
|
git_identity_file=args.git_identity_file,
|
|
81
113
|
oauth_token=args.gh_token,
|
|
82
114
|
)
|
|
@@ -30,7 +30,7 @@ class LogsCommand(APIBaseCommand):
|
|
|
30
30
|
type=int,
|
|
31
31
|
default=0,
|
|
32
32
|
)
|
|
33
|
-
self._parser.add_argument("run_name").completer = RunNameCompleter(all=True)
|
|
33
|
+
self._parser.add_argument("run_name").completer = RunNameCompleter(all=True) # type: ignore[attr-defined]
|
|
34
34
|
|
|
35
35
|
def _command(self, args: argparse.Namespace):
|
|
36
36
|
super()._command(args)
|
|
@@ -26,7 +26,7 @@ class MetricsCommand(APIBaseCommand):
|
|
|
26
26
|
|
|
27
27
|
def _register(self):
|
|
28
28
|
super()._register()
|
|
29
|
-
self._parser.add_argument("run_name").completer = RunNameCompleter()
|
|
29
|
+
self._parser.add_argument("run_name").completer = RunNameCompleter() # type: ignore[attr-defined]
|
|
30
30
|
self._parser.add_argument(
|
|
31
31
|
"-w",
|
|
32
32
|
"--watch",
|
|
@@ -3,7 +3,11 @@ from pathlib import Path
|
|
|
3
3
|
from typing import List
|
|
4
4
|
|
|
5
5
|
from dstack._internal.cli.commands import APIBaseCommand
|
|
6
|
-
from dstack._internal.cli.services.
|
|
6
|
+
from dstack._internal.cli.services.args import cpu_spec, disk_spec, gpu_spec
|
|
7
|
+
from dstack._internal.cli.services.configurators.run import (
|
|
8
|
+
BaseRunConfigurator,
|
|
9
|
+
)
|
|
10
|
+
from dstack._internal.cli.services.profile import register_profile_args
|
|
7
11
|
from dstack._internal.cli.utils.common import console
|
|
8
12
|
from dstack._internal.cli.utils.gpu import print_gpu_json, print_gpu_table
|
|
9
13
|
from dstack._internal.cli.utils.run import print_offers_json, print_run_plan
|
|
@@ -18,11 +22,8 @@ class OfferConfigurator(BaseRunConfigurator):
|
|
|
18
22
|
TYPE = ApplyConfigurationType.TASK
|
|
19
23
|
|
|
20
24
|
@classmethod
|
|
21
|
-
def register_args(
|
|
22
|
-
cls
|
|
23
|
-
parser: argparse.ArgumentParser,
|
|
24
|
-
):
|
|
25
|
-
super().register_args(parser, default_max_offers=50)
|
|
25
|
+
def register_args(cls, parser: argparse.ArgumentParser):
|
|
26
|
+
configuration_group = parser.add_argument_group(f"{cls.TYPE.value} Options")
|
|
26
27
|
parser.add_argument(
|
|
27
28
|
"--group-by",
|
|
28
29
|
action="append",
|
|
@@ -33,6 +34,43 @@ class OfferConfigurator(BaseRunConfigurator):
|
|
|
33
34
|
"Can be repeated or comma-separated (e.g. [code]--group-by gpu,backend[/code])."
|
|
34
35
|
),
|
|
35
36
|
)
|
|
37
|
+
configuration_group.add_argument(
|
|
38
|
+
"-n",
|
|
39
|
+
"--name",
|
|
40
|
+
dest="run_name",
|
|
41
|
+
help="The name of the run. If not specified, a random name is assigned",
|
|
42
|
+
)
|
|
43
|
+
configuration_group.add_argument(
|
|
44
|
+
"--max-offers",
|
|
45
|
+
help="Number of offers to show in the run plan",
|
|
46
|
+
type=int,
|
|
47
|
+
default=50,
|
|
48
|
+
)
|
|
49
|
+
cls.register_env_args(configuration_group)
|
|
50
|
+
configuration_group.add_argument(
|
|
51
|
+
"--cpu",
|
|
52
|
+
type=cpu_spec,
|
|
53
|
+
help="Request CPU for the run. "
|
|
54
|
+
"The format is [code]ARCH[/]:[code]COUNT[/] (all parts are optional)",
|
|
55
|
+
dest="cpu_spec",
|
|
56
|
+
metavar="SPEC",
|
|
57
|
+
)
|
|
58
|
+
configuration_group.add_argument(
|
|
59
|
+
"--gpu",
|
|
60
|
+
type=gpu_spec,
|
|
61
|
+
help="Request GPU for the run. "
|
|
62
|
+
"The format is [code]NAME[/]:[code]COUNT[/]:[code]MEMORY[/] (all parts are optional)",
|
|
63
|
+
dest="gpu_spec",
|
|
64
|
+
metavar="SPEC",
|
|
65
|
+
)
|
|
66
|
+
configuration_group.add_argument(
|
|
67
|
+
"--disk",
|
|
68
|
+
type=disk_spec,
|
|
69
|
+
help="Request the size range of disk for the run. Example [code]--disk 100GB..[/].",
|
|
70
|
+
metavar="RANGE",
|
|
71
|
+
dest="disk_spec",
|
|
72
|
+
)
|
|
73
|
+
register_profile_args(parser)
|
|
36
74
|
|
|
37
75
|
|
|
38
76
|
class OfferCommand(APIBaseCommand):
|
|
@@ -117,7 +155,7 @@ class OfferCommand(APIBaseCommand):
|
|
|
117
155
|
|
|
118
156
|
return processed
|
|
119
157
|
|
|
120
|
-
def _list_gpus(self, args:
|
|
158
|
+
def _list_gpus(self, args: argparse.Namespace, run_spec: RunSpec) -> List[GpuGroup]:
|
|
121
159
|
group_by = [g for g in args.group_by if g != "gpu"] or None
|
|
122
160
|
return self.api.client.gpus.list_gpus(
|
|
123
161
|
self.api.project,
|
|
@@ -124,8 +124,8 @@ class ProjectCommand(BaseCommand):
|
|
|
124
124
|
table.add_column("USER", style="grey58")
|
|
125
125
|
table.add_column("DEFAULT", justify="center")
|
|
126
126
|
|
|
127
|
-
for
|
|
128
|
-
|
|
127
|
+
for project_config in config_manager.list_project_configs():
|
|
128
|
+
project_name = project_config.name
|
|
129
129
|
is_default = project_name == default_project.name if default_project else False
|
|
130
130
|
|
|
131
131
|
# Get username from API
|
|
@@ -29,7 +29,7 @@ class SecretCommand(APIBaseCommand):
|
|
|
29
29
|
get_parser.add_argument(
|
|
30
30
|
"name",
|
|
31
31
|
help="The name of the secret",
|
|
32
|
-
).completer = SecretNameCompleter()
|
|
32
|
+
).completer = SecretNameCompleter() # type: ignore[attr-defined]
|
|
33
33
|
get_parser.set_defaults(subfunc=self._get)
|
|
34
34
|
|
|
35
35
|
set_parser = subparsers.add_parser(
|
|
@@ -53,7 +53,7 @@ class SecretCommand(APIBaseCommand):
|
|
|
53
53
|
delete_parser.add_argument(
|
|
54
54
|
"name",
|
|
55
55
|
help="The name of the secret",
|
|
56
|
-
).completer = SecretNameCompleter()
|
|
56
|
+
).completer = SecretNameCompleter() # type: ignore[attr-defined]
|
|
57
57
|
delete_parser.add_argument(
|
|
58
58
|
"-y", "--yes", help="Don't ask for confirmation", action="store_true"
|
|
59
59
|
)
|
|
@@ -74,7 +74,7 @@ class ServerCommand(BaseCommand):
|
|
|
74
74
|
uvicorn_log_level = os.getenv("DSTACK_SERVER_UVICORN_LOG_LEVEL", "ERROR").lower()
|
|
75
75
|
reload_disabled = os.getenv("DSTACK_SERVER_RELOAD_DISABLED") is not None
|
|
76
76
|
|
|
77
|
-
uvicorn.run(
|
|
77
|
+
uvicorn.run( # type: ignore[unbound-variable]
|
|
78
78
|
"dstack._internal.server.main:app",
|
|
79
79
|
host=args.host,
|
|
80
80
|
port=args.port,
|
|
@@ -14,7 +14,7 @@ class StopCommand(APIBaseCommand):
|
|
|
14
14
|
super()._register()
|
|
15
15
|
self._parser.add_argument("-x", "--abort", action="store_true")
|
|
16
16
|
self._parser.add_argument("-y", "--yes", action="store_true")
|
|
17
|
-
self._parser.add_argument("run_name").completer = RunNameCompleter()
|
|
17
|
+
self._parser.add_argument("run_name").completer = RunNameCompleter() # type: ignore[attr-defined]
|
|
18
18
|
|
|
19
19
|
def _command(self, args: argparse.Namespace):
|
|
20
20
|
super()._command(args)
|
|
@@ -48,7 +48,7 @@ class VolumeCommand(APIBaseCommand):
|
|
|
48
48
|
delete_parser.add_argument(
|
|
49
49
|
"name",
|
|
50
50
|
help="The name of the volume",
|
|
51
|
-
).completer = VolumeNameCompleter()
|
|
51
|
+
).completer = VolumeNameCompleter() # type: ignore[attr-defined]
|
|
52
52
|
delete_parser.add_argument(
|
|
53
53
|
"-y", "--yes", help="Don't ask for confirmation", action="store_true"
|
|
54
54
|
)
|
dstack/_internal/cli/main.py
CHANGED
|
@@ -41,8 +41,8 @@ def main():
|
|
|
41
41
|
|
|
42
42
|
parser = argparse.ArgumentParser(
|
|
43
43
|
description=(
|
|
44
|
-
"Not sure where to start?
|
|
45
|
-
"Define a [code].dstack.yml[/] configuration file and run it via [code]dstack apply[/]\n"
|
|
44
|
+
"Not sure where to start?"
|
|
45
|
+
" Define a [code].dstack.yml[/] configuration file and run it via [code]dstack apply[/]\n"
|
|
46
46
|
),
|
|
47
47
|
formatter_class=RichHelpFormatter,
|
|
48
48
|
epilog=(
|
|
@@ -87,5 +87,5 @@ class ProjectNameCompleter(BaseCompleter):
|
|
|
87
87
|
|
|
88
88
|
def __call__(self, prefix: str, parsed_args: argparse.Namespace, **kwargs) -> List[str]:
|
|
89
89
|
argcomplete.debug(f"{self.__class__.__name__}: Listing projects from ConfigManager")
|
|
90
|
-
projects = ConfigManager().
|
|
91
|
-
return [p for p in projects if p.startswith(prefix)]
|
|
90
|
+
projects = ConfigManager().list_project_configs()
|
|
91
|
+
return [p.name for p in projects if p.name.startswith(prefix)]
|
|
@@ -24,7 +24,9 @@ from dstack._internal.core.models.configurations import (
|
|
|
24
24
|
APPLY_STDIN_NAME = "-"
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
apply_configurators_mapping: Dict[
|
|
27
|
+
apply_configurators_mapping: Dict[
|
|
28
|
+
ApplyConfigurationType, Type[BaseApplyConfigurator[AnyApplyConfiguration]]
|
|
29
|
+
] = {
|
|
28
30
|
cls.TYPE: cls
|
|
29
31
|
for cls in [
|
|
30
32
|
DevEnvironmentConfigurator,
|
|
@@ -47,7 +49,9 @@ run_configurators_mapping: Dict[ApplyConfigurationType, Type[BaseRunConfigurator
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
|
|
50
|
-
def get_apply_configurator_class(
|
|
52
|
+
def get_apply_configurator_class(
|
|
53
|
+
configurator_type: str,
|
|
54
|
+
) -> Type[BaseApplyConfigurator[AnyApplyConfiguration]]:
|
|
51
55
|
return apply_configurators_mapping[ApplyConfigurationType(configurator_type)]
|
|
52
56
|
|
|
53
57
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import List,
|
|
4
|
+
from typing import Generic, List, TypeVar, Union, cast
|
|
5
5
|
|
|
6
6
|
from dstack._internal.cli.services.args import env_var
|
|
7
7
|
from dstack._internal.core.errors import ConfigurationError
|
|
@@ -10,13 +10,14 @@ from dstack._internal.core.models.configurations import (
|
|
|
10
10
|
ApplyConfigurationType,
|
|
11
11
|
)
|
|
12
12
|
from dstack._internal.core.models.envs import Env, EnvSentinel, EnvVarTuple
|
|
13
|
-
from dstack._internal.core.models.repos.base import Repo
|
|
14
13
|
from dstack.api._public import Client
|
|
15
14
|
|
|
16
15
|
ArgsParser = Union[argparse._ArgumentGroup, argparse.ArgumentParser]
|
|
17
16
|
|
|
17
|
+
ApplyConfigurationT = TypeVar("ApplyConfigurationT", bound=AnyApplyConfiguration)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
class BaseApplyConfigurator(ABC, Generic[ApplyConfigurationT]):
|
|
20
21
|
TYPE: ApplyConfigurationType
|
|
21
22
|
|
|
22
23
|
def __init__(self, api_client: Client):
|
|
@@ -25,12 +26,11 @@ class BaseApplyConfigurator(ABC):
|
|
|
25
26
|
@abstractmethod
|
|
26
27
|
def apply_configuration(
|
|
27
28
|
self,
|
|
28
|
-
conf:
|
|
29
|
+
conf: ApplyConfigurationT,
|
|
29
30
|
configuration_path: str,
|
|
30
31
|
command_args: argparse.Namespace,
|
|
31
32
|
configurator_args: argparse.Namespace,
|
|
32
33
|
unknown_args: List[str],
|
|
33
|
-
repo: Optional[Repo] = None,
|
|
34
34
|
):
|
|
35
35
|
"""
|
|
36
36
|
Implements `dstack apply` for a given configuration type.
|
|
@@ -41,14 +41,13 @@ class BaseApplyConfigurator(ABC):
|
|
|
41
41
|
command_args: The args parsed by `dstack apply`.
|
|
42
42
|
configurator_args: The known args parsed by `cls.get_parser()`.
|
|
43
43
|
unknown_args: The unknown args after parsing by `cls.get_parser()`.
|
|
44
|
-
repo: The repo to use with apply.
|
|
45
44
|
"""
|
|
46
45
|
pass
|
|
47
46
|
|
|
48
47
|
@abstractmethod
|
|
49
48
|
def delete_configuration(
|
|
50
49
|
self,
|
|
51
|
-
conf:
|
|
50
|
+
conf: ApplyConfigurationT,
|
|
52
51
|
configuration_path: str,
|
|
53
52
|
command_args: argparse.Namespace,
|
|
54
53
|
):
|
|
@@ -35,7 +35,6 @@ from dstack._internal.core.models.fleets import (
|
|
|
35
35
|
InstanceGroupPlacement,
|
|
36
36
|
)
|
|
37
37
|
from dstack._internal.core.models.instances import InstanceAvailability, InstanceStatus, SSHKey
|
|
38
|
-
from dstack._internal.core.models.repos.base import Repo
|
|
39
38
|
from dstack._internal.core.services.diff import diff_models
|
|
40
39
|
from dstack._internal.utils.common import local_time
|
|
41
40
|
from dstack._internal.utils.logging import get_logger
|
|
@@ -46,7 +45,7 @@ from dstack.api.utils import load_profile
|
|
|
46
45
|
logger = get_logger(__name__)
|
|
47
46
|
|
|
48
47
|
|
|
49
|
-
class FleetConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator):
|
|
48
|
+
class FleetConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator[FleetConfiguration]):
|
|
50
49
|
TYPE: ApplyConfigurationType = ApplyConfigurationType.FLEET
|
|
51
50
|
|
|
52
51
|
def apply_configuration(
|
|
@@ -56,7 +55,6 @@ class FleetConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator):
|
|
|
56
55
|
command_args: argparse.Namespace,
|
|
57
56
|
configurator_args: argparse.Namespace,
|
|
58
57
|
unknown_args: List[str],
|
|
59
|
-
repo: Optional[Repo] = None,
|
|
60
58
|
):
|
|
61
59
|
self.apply_args(conf, configurator_args, unknown_args)
|
|
62
60
|
profile = load_profile(Path.cwd(), None)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import time
|
|
3
|
-
from typing import List
|
|
3
|
+
from typing import List
|
|
4
4
|
|
|
5
5
|
from rich.table import Table
|
|
6
6
|
|
|
@@ -21,13 +21,12 @@ from dstack._internal.core.models.gateways import (
|
|
|
21
21
|
GatewaySpec,
|
|
22
22
|
GatewayStatus,
|
|
23
23
|
)
|
|
24
|
-
from dstack._internal.core.models.repos.base import Repo
|
|
25
24
|
from dstack._internal.core.services.diff import diff_models
|
|
26
25
|
from dstack._internal.utils.common import local_time
|
|
27
26
|
from dstack.api._public import Client
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
class GatewayConfigurator(BaseApplyConfigurator):
|
|
29
|
+
class GatewayConfigurator(BaseApplyConfigurator[GatewayConfiguration]):
|
|
31
30
|
TYPE: ApplyConfigurationType = ApplyConfigurationType.GATEWAY
|
|
32
31
|
|
|
33
32
|
def apply_configuration(
|
|
@@ -37,7 +36,6 @@ class GatewayConfigurator(BaseApplyConfigurator):
|
|
|
37
36
|
command_args: argparse.Namespace,
|
|
38
37
|
configurator_args: argparse.Namespace,
|
|
39
38
|
unknown_args: List[str],
|
|
40
|
-
repo: Optional[Repo] = None,
|
|
41
39
|
):
|
|
42
40
|
self.apply_args(conf, configurator_args, unknown_args)
|
|
43
41
|
spec = GatewaySpec(
|