anyscale 0.26.51__py3-none-any.whl → 0.26.53__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.
- anyscale/_private/anyscale_client/README.md +1 -1
- anyscale/_private/anyscale_client/anyscale_client.py +178 -46
- anyscale/_private/anyscale_client/common.py +61 -2
- anyscale/_private/anyscale_client/fake_anyscale_client.py +145 -8
- anyscale/_private/docgen/__main__.py +42 -31
- anyscale/_private/docgen/generator.py +63 -28
- anyscale/_private/docgen/models.md +4 -2
- anyscale/_private/sdk/__init__.py +124 -1
- anyscale/_private/workload/workload_config.py +4 -6
- anyscale/_private/workload/workload_sdk.py +105 -12
- anyscale/client/README.md +13 -11
- anyscale/client/openapi_client/__init__.py +3 -3
- anyscale/client/openapi_client/api/default_api.py +512 -316
- anyscale/client/openapi_client/models/__init__.py +3 -3
- anyscale/client/openapi_client/models/aws_config.py +2 -2
- anyscale/client/openapi_client/models/baseimagesenum.py +158 -1
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_url_request.py +31 -3
- anyscale/client/openapi_client/models/cloud_deployment.py +37 -36
- anyscale/client/openapi_client/models/cloud_resource.py +59 -3
- anyscale/client/openapi_client/models/cloud_resource_gcp.py +59 -3
- anyscale/client/openapi_client/models/create_cloud_resource.py +59 -3
- anyscale/client/openapi_client/models/create_cloud_resource_gcp.py +59 -3
- anyscale/client/openapi_client/models/create_resource_notification.py +31 -3
- anyscale/client/openapi_client/models/{decorated_cloud_deployment.py → decorated_cloud_resource.py} +124 -96
- anyscale/client/openapi_client/models/{clouddeployment_list_response.py → decoratedcloudresource_list_response.py} +15 -15
- anyscale/client/openapi_client/models/{decoratedclouddeployment_response.py → decoratedcloudresource_response.py} +11 -11
- anyscale/client/openapi_client/models/file_storage.py +4 -4
- anyscale/client/openapi_client/models/gcp_config.py +2 -2
- anyscale/client/openapi_client/models/ha_job_error_types.py +9 -2
- anyscale/client/openapi_client/models/object_storage.py +4 -4
- anyscale/client/openapi_client/models/ray_runtime_env_config.py +57 -1
- anyscale/client/openapi_client/models/resource_alert_event_type.py +2 -1
- anyscale/client/openapi_client/models/resource_notification.py +29 -1
- anyscale/client/openapi_client/models/supportedbaseimagesenum.py +155 -1
- anyscale/client/openapi_client/models/workload_info.py +31 -3
- anyscale/client/openapi_client/models/workload_state_info.py +29 -1
- anyscale/cloud/models.py +40 -43
- anyscale/commands/cloud_commands.py +93 -88
- anyscale/commands/command_examples.py +37 -49
- anyscale/commands/exec_commands.py +12 -1
- anyscale/commands/list_commands.py +42 -12
- anyscale/commands/project_commands.py +399 -115
- anyscale/commands/schedule_commands.py +22 -11
- anyscale/commands/service_commands.py +11 -6
- anyscale/commands/util.py +94 -1
- anyscale/commands/workspace_commands.py +92 -38
- anyscale/compute_config/__init__.py +1 -1
- anyscale/compute_config/_private/compute_config_sdk.py +8 -11
- anyscale/compute_config/commands.py +3 -3
- anyscale/compute_config/models.py +30 -30
- anyscale/controllers/cloud_controller.py +361 -360
- anyscale/controllers/kubernetes_verifier.py +1 -1
- anyscale/job/_private/job_sdk.py +41 -23
- anyscale/job/models.py +1 -1
- anyscale/project/__init__.py +101 -1
- anyscale/project/_private/project_sdk.py +90 -2
- anyscale/project/commands.py +188 -1
- anyscale/project/models.py +198 -2
- anyscale/sdk/anyscale_client/models/baseimagesenum.py +158 -1
- anyscale/sdk/anyscale_client/models/ray_runtime_env_config.py +57 -1
- anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +155 -1
- anyscale/service/_private/service_sdk.py +2 -1
- anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
- anyscale/util.py +3 -0
- anyscale/utils/runtime_env.py +3 -1
- anyscale/version.py +1 -1
- anyscale/workspace/commands.py +114 -23
- anyscale/workspace/models.py +3 -5
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/METADATA +1 -1
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/RECORD +75 -75
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/WHEEL +0 -0
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/entry_points.txt +0 -0
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/licenses/LICENSE +0 -0
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/licenses/NOTICE +0 -0
- {anyscale-0.26.51.dist-info → anyscale-0.26.53.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@ from typing import Any, Optional
|
|
8
8
|
import click
|
9
9
|
|
10
10
|
from anyscale.cli_logger import BlockLogger
|
11
|
+
from anyscale.commands.util import DeprecatedAnyscaleCommand
|
11
12
|
from anyscale.controllers.list_controller import ListController
|
12
13
|
|
13
14
|
|
@@ -25,26 +26,40 @@ def list_cli() -> None:
|
|
25
26
|
name="clouds",
|
26
27
|
help="[DEPRECATED] List the clouds currently available in your account.",
|
27
28
|
hidden=True,
|
29
|
+
cls=DeprecatedAnyscaleCommand,
|
30
|
+
removal_date="2025-10-01",
|
31
|
+
deprecation_message="`anyscale list clouds` has been deprecated",
|
32
|
+
alternative="use `anyscale cloud list` instead",
|
28
33
|
)
|
29
34
|
@click.option("--json", "show_json", help="Return the results in json", is_flag=True)
|
30
35
|
def list_clouds(show_json: bool) -> None:
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
"""List the clouds currently available in your account.
|
37
|
+
|
38
|
+
DEPRECATED: This command will be removed on 2025-10-01.
|
39
|
+
Use 'anyscale cloud list' instead.
|
40
|
+
"""
|
34
41
|
list_controller = ListController()
|
35
42
|
output = list_controller.list_clouds(json_format=show_json)
|
36
43
|
print(output)
|
37
44
|
|
38
45
|
|
39
46
|
@list_cli.command(
|
40
|
-
name="projects",
|
47
|
+
name="projects",
|
48
|
+
help="[DEPRECATED] List all accessible projects.",
|
49
|
+
hidden=True,
|
50
|
+
cls=DeprecatedAnyscaleCommand,
|
51
|
+
removal_date="2025-10-01",
|
52
|
+
deprecation_message="`anyscale list projects` has been deprecated",
|
53
|
+
alternative="use `anyscale project list` instead",
|
41
54
|
)
|
42
55
|
@click.option("--json", "show_json", help="Return the results in json", is_flag=True)
|
43
56
|
@click.pass_context
|
44
57
|
def project_list(ctx: Any, show_json: bool) -> None: # noqa: ARG001
|
45
|
-
|
46
|
-
|
47
|
-
|
58
|
+
"""List all accessible projects.
|
59
|
+
|
60
|
+
DEPRECATED: This command will be removed on 2025-10-01.
|
61
|
+
Use 'anyscale project list' instead.
|
62
|
+
"""
|
48
63
|
list_controller = ListController()
|
49
64
|
output = list_controller.list_projects(json_format=show_json)
|
50
65
|
print(output)
|
@@ -54,6 +69,10 @@ def project_list(ctx: Any, show_json: bool) -> None: # noqa: ARG001
|
|
54
69
|
name="sessions",
|
55
70
|
help="[DEPRECATED] List all clusters within the current project.",
|
56
71
|
hidden=True,
|
72
|
+
cls=DeprecatedAnyscaleCommand,
|
73
|
+
removal_date="2025-10-01",
|
74
|
+
deprecation_message="`anyscale list sessions` has been deprecated",
|
75
|
+
alternative="use `anyscale cluster list` instead",
|
57
76
|
)
|
58
77
|
@click.option(
|
59
78
|
"--name",
|
@@ -65,9 +84,11 @@ def project_list(ctx: Any, show_json: bool) -> None: # noqa: ARG001
|
|
65
84
|
@click.option("--all", help="List all clusters, including inactive ones.", is_flag=True)
|
66
85
|
@click.option("--json", "show_json", help="Return the results in json", is_flag=True)
|
67
86
|
def session_list(name: Optional[str], all: bool, show_json: bool) -> None: # noqa: A002
|
68
|
-
|
69
|
-
|
70
|
-
|
87
|
+
"""List all clusters within the current project.
|
88
|
+
|
89
|
+
DEPRECATED: This command will be removed on 2025-10-01.
|
90
|
+
Use 'anyscale cluster list' instead.
|
91
|
+
"""
|
71
92
|
list_controller = ListController()
|
72
93
|
output = list_controller.list_sessions(
|
73
94
|
name=name, show_all=all, json_format=show_json
|
@@ -79,7 +100,16 @@ def session_list(name: Optional[str], all: bool, show_json: bool) -> None: # no
|
|
79
100
|
name="ips",
|
80
101
|
help="[DEPRECATED] List IP addresses of head and worker nodes.",
|
81
102
|
context_settings={"ignore_unknown_options": True, "allow_extra_args": True,},
|
103
|
+
cls=DeprecatedAnyscaleCommand,
|
104
|
+
removal_date="2025-10-01",
|
105
|
+
deprecation_message="Listing IPs is not supported on Anyscale",
|
106
|
+
alternative="use the Anyscale console to view cluster node information",
|
82
107
|
)
|
83
108
|
def list_ips() -> None:
|
84
|
-
"""List IP addresses of head and worker nodes.
|
85
|
-
|
109
|
+
"""List IP addresses of head and worker nodes.
|
110
|
+
|
111
|
+
DEPRECATED: This command will be removed on 2025-10-01.
|
112
|
+
Listing IPs is not supported on Anyscale.
|
113
|
+
Use the Anyscale console to view cluster node information.
|
114
|
+
"""
|
115
|
+
raise click.ClickException("Listing IPs is not supported on Anyscale")
|
@@ -1,22 +1,103 @@
|
|
1
|
-
from
|
1
|
+
from io import StringIO
|
2
|
+
from json import dumps as json_dumps
|
3
|
+
import sys
|
4
|
+
from typing import Dict, Optional, Tuple
|
2
5
|
|
3
6
|
import click
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.table import Table
|
9
|
+
import yaml
|
4
10
|
|
5
11
|
import anyscale
|
6
12
|
from anyscale.cli_logger import BlockLogger
|
7
13
|
from anyscale.commands import command_examples
|
8
|
-
from anyscale.commands.
|
14
|
+
from anyscale.commands.list_util import display_list
|
15
|
+
from anyscale.commands.util import (
|
16
|
+
AnyscaleCommand,
|
17
|
+
DeprecatedAnyscaleCommand,
|
18
|
+
NotRequiredIf,
|
19
|
+
)
|
9
20
|
from anyscale.controllers.project_controller import ProjectController
|
10
21
|
from anyscale.project.models import (
|
11
22
|
CreateProjectCollaborator,
|
12
23
|
CreateProjectCollaborators,
|
24
|
+
Project,
|
25
|
+
ProjectMinimal,
|
26
|
+
ProjectSortField,
|
27
|
+
ProjectSortOrder,
|
13
28
|
)
|
14
29
|
from anyscale.project_utils import validate_project_name
|
15
|
-
from anyscale.util import
|
30
|
+
from anyscale.util import (
|
31
|
+
AnyscaleJSONEncoder,
|
32
|
+
get_endpoint,
|
33
|
+
)
|
16
34
|
|
17
35
|
|
18
36
|
log = BlockLogger()
|
19
37
|
|
38
|
+
MAX_PAGE_SIZE = 50
|
39
|
+
NON_INTERACTIVE_DEFAULT_MAX_ITEMS = 10
|
40
|
+
|
41
|
+
|
42
|
+
def _create_project_list_table(show_header: bool) -> Table:
|
43
|
+
table = Table(show_header=show_header, expand=True)
|
44
|
+
# NAME and ID: larger ratios, can wrap but never truncate
|
45
|
+
table.add_column(
|
46
|
+
"NAME", no_wrap=False, overflow="fold", ratio=3, min_width=15,
|
47
|
+
)
|
48
|
+
table.add_column(
|
49
|
+
"ID", no_wrap=False, overflow="fold", ratio=2, min_width=12,
|
50
|
+
)
|
51
|
+
# all other columns will wrap as needed
|
52
|
+
for heading in (
|
53
|
+
"DESCRIPTION",
|
54
|
+
"CREATED AT",
|
55
|
+
"CREATOR",
|
56
|
+
"PARENT CLOUD ID",
|
57
|
+
):
|
58
|
+
table.add_column(
|
59
|
+
heading, no_wrap=False, overflow="fold", ratio=1, min_width=8,
|
60
|
+
)
|
61
|
+
return table
|
62
|
+
|
63
|
+
|
64
|
+
def _format_project_output_data(project: Project) -> Dict[str, str]:
|
65
|
+
return {
|
66
|
+
"name": project.name,
|
67
|
+
"id": project.id,
|
68
|
+
"description": project.description,
|
69
|
+
"created_at": project.created_at,
|
70
|
+
"creator": str(project.creator_id or ""),
|
71
|
+
"parent_cloud_id": str(project.parent_cloud_id or ""),
|
72
|
+
}
|
73
|
+
|
74
|
+
|
75
|
+
def _parse_sort_option(
|
76
|
+
sort: Optional[str],
|
77
|
+
) -> Tuple[Optional[ProjectSortField], ProjectSortOrder]:
|
78
|
+
if not sort:
|
79
|
+
return None, ProjectSortOrder.ASC
|
80
|
+
|
81
|
+
# build case-insensitive map of allowed fields
|
82
|
+
allowed = {f.value.lower(): f.value for f in ProjectSortField.__members__.values()}
|
83
|
+
|
84
|
+
# detect leading '-' for descending
|
85
|
+
if sort.startswith("-"):
|
86
|
+
raw = sort[1:]
|
87
|
+
order = ProjectSortOrder.DESC
|
88
|
+
else:
|
89
|
+
raw = sort
|
90
|
+
order = ProjectSortOrder.ASC
|
91
|
+
|
92
|
+
key = raw.lower()
|
93
|
+
if key not in allowed:
|
94
|
+
allowed_names = ", ".join(sorted(allowed.values()))
|
95
|
+
raise click.BadParameter(
|
96
|
+
f"Invalid sort field '{raw}'. Allowed fields: {allowed_names}"
|
97
|
+
)
|
98
|
+
|
99
|
+
return ProjectSortField(allowed[key]), order
|
100
|
+
|
20
101
|
|
21
102
|
@click.group(
|
22
103
|
"project",
|
@@ -28,49 +109,316 @@ def project_cli() -> None:
|
|
28
109
|
|
29
110
|
|
30
111
|
@project_cli.command(
|
31
|
-
name="
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
is_limited_support=True,
|
36
|
-
legacy_prefix="anyscale project",
|
112
|
+
name="get",
|
113
|
+
help="Get details of a project.",
|
114
|
+
cls=AnyscaleCommand,
|
115
|
+
example=command_examples.PROJECT_GET_EXAMPLE,
|
37
116
|
)
|
38
117
|
@click.option(
|
39
|
-
"--
|
118
|
+
"--id", "-i", type=str, required=True, help="ID of the project.",
|
40
119
|
)
|
41
|
-
@click.option("--json", help="Format output as JSON.", is_flag=True)
|
42
120
|
@click.option(
|
43
|
-
"--
|
44
|
-
"-
|
45
|
-
help="[Deprecated] List projects created by any user.",
|
121
|
+
"--json",
|
122
|
+
"-j",
|
46
123
|
is_flag=True,
|
47
|
-
default=
|
48
|
-
|
124
|
+
default=False,
|
125
|
+
help="Output the details in a structured JSON format.",
|
126
|
+
)
|
127
|
+
def get(id: str, json: bool = False): # noqa: A002
|
128
|
+
try:
|
129
|
+
project: Project = anyscale.project.get(project_id=id)
|
130
|
+
except ValueError as e:
|
131
|
+
log.error(f"Error getting project details: {e}")
|
132
|
+
sys.exit(1)
|
133
|
+
|
134
|
+
console = Console()
|
135
|
+
if json:
|
136
|
+
json_str = json_dumps(project.to_dict(), indent=2, cls=AnyscaleJSONEncoder)
|
137
|
+
console.print_json(json=json_str)
|
138
|
+
else:
|
139
|
+
stream = StringIO()
|
140
|
+
yaml.dump(project.to_dict(), stream, sort_keys=False)
|
141
|
+
console.print(stream.getvalue(), end="")
|
142
|
+
|
143
|
+
|
144
|
+
@project_cli.command(
|
145
|
+
name="list",
|
146
|
+
help="List all projects with optional filters.",
|
147
|
+
cls=AnyscaleCommand,
|
148
|
+
example=command_examples.PROJECT_LIST_EXAMPLE,
|
49
149
|
)
|
50
|
-
@click.option("--created-by-me", help="List projects created by me only.", is_flag=True)
|
51
150
|
@click.option(
|
52
|
-
"--
|
53
|
-
|
54
|
-
|
151
|
+
"--name", "-n", type=str, help="A string to filter projects by name.",
|
152
|
+
)
|
153
|
+
@click.option(
|
154
|
+
"--creator", "-u", type=str, help="The ID of a creator to filter projects.",
|
155
|
+
)
|
156
|
+
@click.option(
|
157
|
+
"--cloud", "-c", type=str, help="The ID of a parent cloud to filter projects.",
|
158
|
+
)
|
159
|
+
@click.option(
|
160
|
+
"--include-defaults/--exclude-defaults",
|
161
|
+
default=True,
|
162
|
+
show_default=True,
|
163
|
+
help="Whether to include default projects.",
|
164
|
+
)
|
165
|
+
@click.option(
|
166
|
+
"--max-items", type=int, help="The maximum number of projects to return.",
|
167
|
+
)
|
168
|
+
@click.option(
|
169
|
+
"--page-size",
|
55
170
|
type=int,
|
56
|
-
|
57
|
-
|
171
|
+
default=10,
|
172
|
+
show_default=True,
|
173
|
+
help="The number of projects to return per page.",
|
174
|
+
)
|
175
|
+
@click.option(
|
176
|
+
"--sort",
|
177
|
+
help=(
|
178
|
+
"Sort by FIELD (prefix with '-' for desc). "
|
179
|
+
f"Allowed: {', '.join(ProjectSortField.__members__.values())}"
|
180
|
+
),
|
181
|
+
)
|
182
|
+
@click.option(
|
183
|
+
"--interactive/--no-interactive",
|
184
|
+
default=True,
|
185
|
+
show_default=True,
|
186
|
+
help="Use interactive paging.",
|
187
|
+
)
|
188
|
+
@click.option(
|
189
|
+
"--json",
|
190
|
+
"-j",
|
191
|
+
is_flag=True,
|
192
|
+
default=False,
|
193
|
+
help="Output the list in a structured JSON format.",
|
58
194
|
)
|
59
195
|
def list( # noqa: A001
|
196
|
+
*,
|
197
|
+
name: Optional[str] = None,
|
198
|
+
creator: Optional[str] = None,
|
199
|
+
cloud: Optional[str] = None,
|
200
|
+
include_defaults: bool = True,
|
201
|
+
max_items: Optional[int] = None,
|
202
|
+
page_size: Optional[int] = None,
|
203
|
+
sort: Optional[str] = None,
|
204
|
+
interactive: bool = True,
|
205
|
+
json: bool = False,
|
206
|
+
):
|
207
|
+
|
208
|
+
if max_items is not None and interactive:
|
209
|
+
raise click.UsageError("--max-items only allowed with --no-interactive")
|
210
|
+
|
211
|
+
sort_field, sort_order = _parse_sort_option(sort)
|
212
|
+
|
213
|
+
# normalize max_items
|
214
|
+
effective_max = max_items
|
215
|
+
if not interactive and effective_max is None:
|
216
|
+
stderr = Console(stderr=True)
|
217
|
+
stderr.print(
|
218
|
+
f"Defaulting to {NON_INTERACTIVE_DEFAULT_MAX_ITEMS} items in batch mode; "
|
219
|
+
"use --max-items to override."
|
220
|
+
)
|
221
|
+
effective_max = NON_INTERACTIVE_DEFAULT_MAX_ITEMS
|
222
|
+
|
223
|
+
console = Console()
|
224
|
+
stderr = Console(stderr=True)
|
225
|
+
|
226
|
+
# diagnostics
|
227
|
+
stderr.print("[bold]Listing projects with:[/]")
|
228
|
+
stderr.print(f"• name_contains = {name or '<any>'}")
|
229
|
+
stderr.print(f"• creator_id = {creator or '<any>'}")
|
230
|
+
stderr.print(f"• parent_cloud_id = {cloud or '<any>'}")
|
231
|
+
stderr.print(f"• include defaults = {include_defaults}")
|
232
|
+
stderr.print(f"• sort-field = {sort_field or '<none>'}")
|
233
|
+
stderr.print(f"• sort-order = {sort_order or '<none>'}")
|
234
|
+
stderr.print(f"• mode = {'interactive' if interactive else 'batch'}")
|
235
|
+
stderr.print(f"• per-page limit = {page_size}")
|
236
|
+
stderr.print(f"• max-items total = {effective_max or 'all'}")
|
237
|
+
stderr.print(f"\nView your Projects in the UI at {get_endpoint('/projects')}\n")
|
238
|
+
|
239
|
+
# choose formatter
|
240
|
+
if json:
|
241
|
+
|
242
|
+
def formatter(project):
|
243
|
+
return ProjectMinimal.from_dict(project.to_dict()).to_dict()
|
244
|
+
|
245
|
+
else:
|
246
|
+
formatter = _format_project_output_data
|
247
|
+
|
248
|
+
total = 0
|
249
|
+
try:
|
250
|
+
iterator = anyscale.project.list(
|
251
|
+
name_contains=name,
|
252
|
+
creator_id=creator,
|
253
|
+
parent_cloud_id=cloud,
|
254
|
+
include_defaults=include_defaults,
|
255
|
+
max_items=effective_max,
|
256
|
+
page_size=page_size,
|
257
|
+
sort_field=sort_field,
|
258
|
+
sort_order=sort_order,
|
259
|
+
)
|
260
|
+
total = display_list(
|
261
|
+
iterator=iter(iterator),
|
262
|
+
item_formatter=formatter,
|
263
|
+
table_creator=_create_project_list_table,
|
264
|
+
json_output=json,
|
265
|
+
page_size=page_size or MAX_PAGE_SIZE,
|
266
|
+
interactive=interactive,
|
267
|
+
max_items=effective_max,
|
268
|
+
console=console,
|
269
|
+
)
|
270
|
+
|
271
|
+
if not json:
|
272
|
+
if total > 0:
|
273
|
+
stderr.print(f"\nFetched {total} projects.")
|
274
|
+
else:
|
275
|
+
stderr.print("\nNo projects found.")
|
276
|
+
except Exception as e: # noqa: BLE001
|
277
|
+
log.error(f"Failed to list projects: {e}")
|
278
|
+
sys.exit(1)
|
279
|
+
|
280
|
+
|
281
|
+
@project_cli.command(
|
282
|
+
name="create",
|
283
|
+
help="Create a new project.",
|
284
|
+
cls=AnyscaleCommand,
|
285
|
+
example=command_examples.PROJECT_CREATE_EXAMPLE,
|
286
|
+
)
|
287
|
+
@click.option(
|
288
|
+
"--name", "-n", type=str, required=True, help="Name of the project.",
|
289
|
+
)
|
290
|
+
@click.option(
|
291
|
+
"--cloud", "-c", type=str, required=True, help="Parent cloud ID for the project.",
|
292
|
+
)
|
293
|
+
@click.option(
|
294
|
+
"--description", "-d", type=str, help="Description of the project.",
|
295
|
+
)
|
296
|
+
@click.option(
|
297
|
+
"--initial-cluster-config",
|
298
|
+
"-f",
|
299
|
+
type=str,
|
300
|
+
help="Initial cluster config for the project.",
|
301
|
+
)
|
302
|
+
def create(
|
60
303
|
name: str,
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
304
|
+
cloud: str,
|
305
|
+
*,
|
306
|
+
description: Optional[str] = None,
|
307
|
+
initial_cluster_config: Optional[str] = None,
|
65
308
|
) -> None:
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
309
|
+
try:
|
310
|
+
project_id: str = anyscale.project.create(
|
311
|
+
name,
|
312
|
+
cloud,
|
313
|
+
description=description or "",
|
314
|
+
initial_cluster_config=initial_cluster_config,
|
71
315
|
)
|
72
|
-
|
73
|
-
|
316
|
+
except ValueError as e:
|
317
|
+
log.error(f"Error creating project: {e}")
|
318
|
+
sys.exit(1)
|
319
|
+
|
320
|
+
log.info(f"Created project '{name}' with ID: {project_id}")
|
321
|
+
|
322
|
+
|
323
|
+
@project_cli.command(
|
324
|
+
name="delete",
|
325
|
+
help="Delete a project.",
|
326
|
+
cls=AnyscaleCommand,
|
327
|
+
example=command_examples.PROJECT_DELETE_EXAMPLE,
|
328
|
+
)
|
329
|
+
@click.option(
|
330
|
+
"--id", "-i", type=str, required=True, help="ID of the project to delete.",
|
331
|
+
)
|
332
|
+
def delete(id: str): # noqa: A002
|
333
|
+
try:
|
334
|
+
anyscale.project.delete(id)
|
335
|
+
except ValueError as e:
|
336
|
+
log.error(f"Error deleting project: {e}")
|
337
|
+
sys.exit(1)
|
338
|
+
|
339
|
+
log.info(f"Deleted project '{id}'")
|
340
|
+
|
341
|
+
|
342
|
+
@project_cli.command(
|
343
|
+
name="get-default",
|
344
|
+
help="Get the default project for a cloud.",
|
345
|
+
cls=AnyscaleCommand,
|
346
|
+
example=command_examples.PROJECT_GET_DEFAULT_EXAMPLE,
|
347
|
+
)
|
348
|
+
@click.option(
|
349
|
+
"--cloud", "-c", type=str, required=True, help="Parent cloud ID for the project.",
|
350
|
+
)
|
351
|
+
@click.option(
|
352
|
+
"--json",
|
353
|
+
"-j",
|
354
|
+
is_flag=True,
|
355
|
+
default=False,
|
356
|
+
help="Output the project in a structured JSON format.",
|
357
|
+
)
|
358
|
+
def get_default(cloud: str, json: bool = False):
|
359
|
+
try:
|
360
|
+
project: Project = anyscale.project.get_default(cloud)
|
361
|
+
except ValueError as e:
|
362
|
+
log.error(f"Error getting default project for cloud '{cloud}': {e}")
|
363
|
+
sys.exit(1)
|
364
|
+
|
365
|
+
console = Console()
|
366
|
+
if json:
|
367
|
+
json_str = json_dumps(project.to_dict(), indent=2, cls=AnyscaleJSONEncoder)
|
368
|
+
console.print_json(json=json_str)
|
369
|
+
else:
|
370
|
+
stream = StringIO()
|
371
|
+
yaml.dump(project.to_dict(), stream, sort_keys=False)
|
372
|
+
console.print(stream.getvalue(), end="")
|
373
|
+
|
374
|
+
|
375
|
+
@project_cli.command(
|
376
|
+
name="add-collaborators",
|
377
|
+
help="Add collaborators to the project.",
|
378
|
+
cls=AnyscaleCommand,
|
379
|
+
example=command_examples.PROJECT_ADD_COLLABORATORS_EXAMPLE,
|
380
|
+
)
|
381
|
+
@click.option(
|
382
|
+
"--cloud",
|
383
|
+
"-c",
|
384
|
+
help="Name of the cloud that the project belongs to.",
|
385
|
+
required=True,
|
386
|
+
)
|
387
|
+
@click.option(
|
388
|
+
"--project",
|
389
|
+
"-p",
|
390
|
+
help="Name of the project to add collaborators to.",
|
391
|
+
required=True,
|
392
|
+
)
|
393
|
+
@click.option(
|
394
|
+
"--users-file",
|
395
|
+
help="Path to a YAML file containing a list of users to add to the project.",
|
396
|
+
required=True,
|
397
|
+
)
|
398
|
+
def add_collaborators(cloud: str, project: str, users_file: str) -> None:
|
399
|
+
collaborators = CreateProjectCollaborators.from_yaml(users_file)
|
400
|
+
|
401
|
+
try:
|
402
|
+
anyscale.project.add_collaborators(
|
403
|
+
cloud=cloud,
|
404
|
+
project=project,
|
405
|
+
collaborators=[
|
406
|
+
CreateProjectCollaborator(**collaborator)
|
407
|
+
for collaborator in collaborators.collaborators
|
408
|
+
],
|
409
|
+
)
|
410
|
+
except ValueError as e:
|
411
|
+
log.error(f"Error adding collaborators to project: {e}")
|
412
|
+
return
|
413
|
+
|
414
|
+
log.info(
|
415
|
+
f"Successfully added {len(collaborators.collaborators)} collaborators to project {project}."
|
416
|
+
)
|
417
|
+
|
418
|
+
|
419
|
+
# ================================================
|
420
|
+
# LEGACY CODE
|
421
|
+
# ================================================
|
74
422
|
|
75
423
|
|
76
424
|
def _validate_project_name(ctx, param, value) -> str: # noqa: ARG001
|
@@ -97,6 +445,10 @@ def _default_project_name() -> str:
|
|
97
445
|
"[DEPRECATED] Create a new project or attach this directory to an existing project."
|
98
446
|
),
|
99
447
|
hidden=True,
|
448
|
+
cls=DeprecatedAnyscaleCommand,
|
449
|
+
removal_date="2025-10-01",
|
450
|
+
deprecation_message="`anyscale init` has been deprecated",
|
451
|
+
alternative="use `anyscale project create` to create a new project",
|
100
452
|
)
|
101
453
|
@click.option(
|
102
454
|
"--project-id",
|
@@ -130,10 +482,11 @@ def anyscale_init(
|
|
130
482
|
config: Optional[str],
|
131
483
|
requirements: Optional[str],
|
132
484
|
) -> None:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
485
|
+
"""Create a new project or attach this directory to an existing project.
|
486
|
+
|
487
|
+
DEPRECATED: This command will be removed on 2025-10-01.
|
488
|
+
Use 'anyscale project create' to create a new project.
|
489
|
+
"""
|
137
490
|
if (project_id and name) or not (project_id or name):
|
138
491
|
raise click.BadArgumentUsage(
|
139
492
|
"Only one of project_id and name must be provided."
|
@@ -147,6 +500,10 @@ def anyscale_init(
|
|
147
500
|
name="init",
|
148
501
|
help="[DEPRECATED] Create a new project or attach this directory to an existing project.",
|
149
502
|
hidden=True,
|
503
|
+
cls=DeprecatedAnyscaleCommand,
|
504
|
+
removal_date="2025-10-01",
|
505
|
+
deprecation_message="`anyscale project init` has been deprecated",
|
506
|
+
alternative="use `anyscale project create` to create a new project and specify a project id or name for the other Anyscale CLI commands",
|
150
507
|
)
|
151
508
|
@click.option(
|
152
509
|
"--project-id",
|
@@ -166,11 +523,11 @@ def anyscale_init(
|
|
166
523
|
default=_default_project_name(),
|
167
524
|
)
|
168
525
|
def init(project_id: Optional[str], name: Optional[str],) -> None:
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
526
|
+
"""Create a new project or attach this directory to an existing project.
|
527
|
+
|
528
|
+
DEPRECATED: This command will be removed on 2025-10-01.
|
529
|
+
Use 'anyscale project create' to create a new project.
|
530
|
+
"""
|
174
531
|
if (project_id and name) or not (project_id or name):
|
175
532
|
raise click.BadArgumentUsage(
|
176
533
|
"Only one of --project-id and --name must be provided."
|
@@ -178,76 +535,3 @@ def init(project_id: Optional[str], name: Optional[str],) -> None:
|
|
178
535
|
|
179
536
|
project_controller = ProjectController()
|
180
537
|
project_controller.init(project_id, name, None, None)
|
181
|
-
|
182
|
-
|
183
|
-
@project_cli.command(
|
184
|
-
name="create",
|
185
|
-
help="Create a new project.",
|
186
|
-
cls=LegacyAnyscaleCommand,
|
187
|
-
is_limited_support=True,
|
188
|
-
legacy_prefix="anyscale project",
|
189
|
-
)
|
190
|
-
@click.option(
|
191
|
-
"--name",
|
192
|
-
"-n",
|
193
|
-
help="Project name.",
|
194
|
-
callback=_validate_project_name,
|
195
|
-
prompt=True,
|
196
|
-
default=_default_project_name(),
|
197
|
-
)
|
198
|
-
@click.option(
|
199
|
-
"--parent-cloud-id",
|
200
|
-
required=False,
|
201
|
-
default=None,
|
202
|
-
help=(
|
203
|
-
"Cloud id that this project is associated with. This argument "
|
204
|
-
"is only relevant if cloud isolation is enabled."
|
205
|
-
),
|
206
|
-
)
|
207
|
-
def create(name: str, parent_cloud_id: str) -> None:
|
208
|
-
project_controller = ProjectController()
|
209
|
-
project_controller.create(name, parent_cloud_id)
|
210
|
-
|
211
|
-
|
212
|
-
@project_cli.command(
|
213
|
-
name="add-collaborators",
|
214
|
-
help="Add collaborators to the project.",
|
215
|
-
cls=AnyscaleCommand,
|
216
|
-
example=command_examples.PROJECT_ADD_COLLABORATORS_EXAMPLE,
|
217
|
-
)
|
218
|
-
@click.option(
|
219
|
-
"--cloud",
|
220
|
-
"-c",
|
221
|
-
help="Name of the cloud that the project belongs to.",
|
222
|
-
required=True,
|
223
|
-
)
|
224
|
-
@click.option(
|
225
|
-
"--project",
|
226
|
-
"-p",
|
227
|
-
help="Name of the project to add collaborators to.",
|
228
|
-
required=True,
|
229
|
-
)
|
230
|
-
@click.option(
|
231
|
-
"--users-file",
|
232
|
-
help="Path to a YAML file containing a list of users to add to the project.",
|
233
|
-
required=True,
|
234
|
-
)
|
235
|
-
def add_collaborators(cloud: str, project: str, users_file: str,) -> None:
|
236
|
-
collaborators = CreateProjectCollaborators.from_yaml(users_file)
|
237
|
-
|
238
|
-
try:
|
239
|
-
anyscale.project.add_collaborators(
|
240
|
-
cloud=cloud,
|
241
|
-
project=project,
|
242
|
-
collaborators=[
|
243
|
-
CreateProjectCollaborator(**collaborator)
|
244
|
-
for collaborator in collaborators.collaborators
|
245
|
-
],
|
246
|
-
)
|
247
|
-
except ValueError as e:
|
248
|
-
log.error(f"Error adding collaborators to project: {e}")
|
249
|
-
return
|
250
|
-
|
251
|
-
log.info(
|
252
|
-
f"Successfully added {len(collaborators.collaborators)} collaborators to project {project}."
|
253
|
-
)
|