anyscale 0.25.1__py3-none-any.whl → 0.25.2__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 (39) hide show
  1. anyscale/__init__.py +10 -0
  2. anyscale/_private/anyscale_client/anyscale_client.py +35 -9
  3. anyscale/_private/anyscale_client/common.py +32 -3
  4. anyscale/_private/anyscale_client/fake_anyscale_client.py +69 -6
  5. anyscale/_private/docgen/__main__.py +9 -2
  6. anyscale/_private/docgen/api.md +13 -0
  7. anyscale/_private/docgen/models.md +3 -3
  8. anyscale/client/README.md +1 -0
  9. anyscale/client/openapi_client/api/default_api.py +119 -0
  10. anyscale/client/openapi_client/models/baseimagesenum.py +43 -1
  11. anyscale/client/openapi_client/models/jobs_sort_field.py +1 -2
  12. anyscale/client/openapi_client/models/sessions_sort_field.py +1 -2
  13. anyscale/client/openapi_client/models/supportedbaseimagesenum.py +43 -1
  14. anyscale/commands/cloud_commands.py +25 -8
  15. anyscale/commands/service_account_commands.py +65 -8
  16. anyscale/controllers/cloud_controller.py +29 -21
  17. anyscale/controllers/cluster_controller.py +1 -9
  18. anyscale/controllers/job_controller.py +0 -3
  19. anyscale/resource_quota/_private/resource_quota_sdk.py +15 -6
  20. anyscale/sdk/anyscale_client/api/default_api.py +119 -0
  21. anyscale/sdk/anyscale_client/models/baseimagesenum.py +43 -1
  22. anyscale/sdk/anyscale_client/models/jobs_sort_field.py +1 -2
  23. anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +43 -1
  24. anyscale/service_account/__init__.py +88 -0
  25. anyscale/service_account/_private/service_account_sdk.py +101 -0
  26. anyscale/service_account/commands.py +147 -0
  27. anyscale/service_account/models.py +66 -0
  28. anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
  29. anyscale/shared_anyscale_utils/utils/id_gen.py +1 -0
  30. anyscale/util.py +8 -0
  31. anyscale/version.py +1 -1
  32. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/METADATA +1 -1
  33. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/RECORD +38 -35
  34. anyscale/controllers/service_account_controller.py +0 -168
  35. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/LICENSE +0 -0
  36. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/NOTICE +0 -0
  37. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/WHEEL +0 -0
  38. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/entry_points.txt +0 -0
  39. {anyscale-0.25.1.dist-info → anyscale-0.25.2.dist-info}/top_level.txt +0 -0
anyscale/__init__.py CHANGED
@@ -31,6 +31,7 @@ from anyscale import (
31
31
  resource_quota,
32
32
  schedule,
33
33
  service,
34
+ service_account,
34
35
  user,
35
36
  )
36
37
  from anyscale._private.anyscale_client import AnyscaleClient, AnyscaleClientInterface
@@ -52,6 +53,7 @@ from anyscale.resource_quota import ResourceQuotaSDK
52
53
  from anyscale.schedule import ScheduleSDK
53
54
  from anyscale.sdk.anyscale_client.sdk import AnyscaleSDK
54
55
  from anyscale.service import ServiceSDK
56
+ from anyscale.service_account import ServiceAccountSDK
55
57
  from anyscale.user import UserSDK
56
58
  from anyscale.workspace import WorkspaceSDK
57
59
 
@@ -128,6 +130,7 @@ class Anyscale:
128
130
  )
129
131
  self._project_sdk = ProjectSDK(client=self._anyscale_client)
130
132
  self._resource_quota_sdk = ResourceQuotaSDK(client=self._anyscale_client)
133
+ self._service_account_sdk = ServiceAccountSDK(client=self._anyscale_client)
131
134
  self._user_sdk = UserSDK(client=self._anyscale_client)
132
135
  self._workspace_sdk = WorkspaceSDK(client=self._anyscale_client)
133
136
 
@@ -162,6 +165,9 @@ class Anyscale:
162
165
  obj._project_sdk = ProjectSDK( # noqa: SLF001
163
166
  client=client, logger=logger, timer=timer
164
167
  )
168
+ obj._service_account_sdk = ServiceAccountSDK( # noqa: SLF001
169
+ client=client, logger=logger, timer=timer
170
+ )
165
171
  obj._user_sdk = UserSDK( # noqa: SLF001
166
172
  client=client, logger=logger, timer=timer
167
173
  )
@@ -217,6 +223,10 @@ class Anyscale:
217
223
  def resource_quota(self) -> ResourceQuotaSDK: # noqa: F811
218
224
  return self._resource_quota_sdk
219
225
 
226
+ @property
227
+ def service_account(self) -> ServiceAccountSDK: # noqa: F811
228
+ return self._service_account_sdk
229
+
220
230
  @property
221
231
  def user(self) -> UserSDK: # noqa: F811
222
232
  return self._user_sdk
@@ -36,6 +36,8 @@ from anyscale.client.openapi_client.api.default_api import DefaultApi as Interna
36
36
  from anyscale.client.openapi_client.models import (
37
37
  AdminCreatedUser,
38
38
  AdminCreateUser,
39
+ AnyscaleServiceAccount,
40
+ ApiKeyParameters,
39
41
  ArchiveStatus,
40
42
  Cloud,
41
43
  CloudDataBucketAccessMode,
@@ -69,6 +71,7 @@ from anyscale.client.openapi_client.models import (
69
71
  OrganizationInvitation,
70
72
  ResourceQuota,
71
73
  ResourceQuotaStatus,
74
+ ServerSessionToken,
72
75
  SessionSshKey,
73
76
  StartSessionOptions,
74
77
  StopSessionOptions,
@@ -1860,6 +1863,20 @@ class AnyscaleClient(AnyscaleClientInterface):
1860
1863
 
1861
1864
  return filepath
1862
1865
 
1866
+ @handle_api_exceptions
1867
+ def create_api_key(
1868
+ self, duration: float, user_id: Optional[str]
1869
+ ) -> ServerSessionToken:
1870
+ return self._internal_api_client.create_api_key_api_v2_users_create_api_key_post(
1871
+ ApiKeyParameters(user_id=user_id, duration=duration)
1872
+ ).result
1873
+
1874
+ @handle_api_exceptions
1875
+ def rotate_api_key(self, user_id: str) -> None:
1876
+ self._internal_api_client.rotate_api_key_for_user_api_v2_organization_collaborators_rotate_api_key_for_user_user_id_post(
1877
+ user_id
1878
+ )
1879
+
1863
1880
  @handle_api_exceptions
1864
1881
  def admin_batch_create_users(
1865
1882
  self, admin_create_users: List[AdminCreateUser]
@@ -1915,20 +1932,29 @@ class AnyscaleClient(AnyscaleClientInterface):
1915
1932
  ).result
1916
1933
 
1917
1934
  @handle_api_exceptions
1918
- def get_organization_collaborator(self, email: str) -> OrganizationCollaborator:
1935
+ def get_organization_collaborators(
1936
+ self,
1937
+ email: Optional[str] = None,
1938
+ name: Optional[str] = None,
1939
+ is_service_account: Optional[bool] = None,
1940
+ ) -> List[OrganizationCollaborator]:
1919
1941
  results = self._internal_api_client.list_organization_collaborators_api_v2_organization_collaborators_get(
1920
- email=email
1942
+ email=email, name=name, is_service_account=is_service_account
1921
1943
  ).results
1922
1944
 
1923
- if len(results) == 0:
1924
- raise ValueError(f"User with email '{email}' not found.")
1945
+ return results
1925
1946
 
1926
- if len(results) > 1:
1927
- raise ValueError(
1928
- f"Multiple users found for email '{email}'. Please contact Anyscale support."
1929
- )
1947
+ @handle_api_exceptions
1948
+ def delete_organization_collaborator(self, identity_id: str) -> None:
1949
+ self._internal_api_client.remove_organization_collaborator_api_v2_organization_collaborators_identity_id_delete(
1950
+ identity_id
1951
+ )
1930
1952
 
1931
- return results[0]
1953
+ @handle_api_exceptions
1954
+ def create_service_account(self, name: str) -> AnyscaleServiceAccount:
1955
+ return self._internal_api_client.create_service_account_api_v2_users_service_accounts_post(
1956
+ name
1957
+ ).result
1932
1958
 
1933
1959
  @handle_api_exceptions
1934
1960
  def create_resource_quota(
@@ -6,6 +6,7 @@ from anyscale._private.models.image_uri import ImageURI
6
6
  from anyscale.client.openapi_client.models import (
7
7
  AdminCreatedUser,
8
8
  AdminCreateUser,
9
+ AnyscaleServiceAccount,
9
10
  Cloud,
10
11
  ComputeTemplateConfig,
11
12
  CreateCloudCollaborator,
@@ -21,6 +22,7 @@ from anyscale.client.openapi_client.models import (
21
22
  OrganizationInvitation,
22
23
  Project,
23
24
  ResourceQuota,
25
+ ServerSessionToken,
24
26
  WorkspaceDataplaneProxiedArtifacts,
25
27
  )
26
28
  from anyscale.client.openapi_client.models.create_schedule import CreateSchedule
@@ -47,7 +49,7 @@ from anyscale.utils.workspace_notification import WorkspaceNotification
47
49
  # Maybe just make it part of the release process to update it, or fetch the
48
50
  # default builds and get the latest one. The best thing to do is probably
49
51
  # to populate this in the backend.
50
- DEFAULT_RAY_VERSION = "2.40.0" # RAY_RELEASE_UPDATE: update to latest version.
52
+ DEFAULT_RAY_VERSION = "2.41.0" # RAY_RELEASE_UPDATE: update to latest version.
51
53
  DEFAULT_PYTHON_VERSION = "py311"
52
54
  RUNTIME_ENV_PACKAGE_FORMAT = "pkg_{content_hash}.zip"
53
55
 
@@ -614,6 +616,18 @@ class AnyscaleClientInterface(ABC):
614
616
  """Download the aggregated instance usage csv."""
615
617
  raise NotImplementedError
616
618
 
619
+ @abstractmethod
620
+ def create_api_key(
621
+ self, duration: float, user_id: Optional[str]
622
+ ) -> ServerSessionToken:
623
+ """Create a new API key."""
624
+ raise NotImplementedError
625
+
626
+ @abstractmethod
627
+ def rotate_api_key(self, user_id: str) -> None:
628
+ """Rotate the API key for user."""
629
+ raise NotImplementedError
630
+
617
631
  @abstractmethod
618
632
  def admin_batch_create_users(
619
633
  self, admin_create_users: List[AdminCreateUser]
@@ -639,8 +653,23 @@ class AnyscaleClientInterface(ABC):
639
653
  raise NotImplementedError
640
654
 
641
655
  @abstractmethod
642
- def get_organization_collaborator(self, email: str) -> OrganizationCollaborator:
643
- """Get organization collaborator."""
656
+ def get_organization_collaborators(
657
+ self,
658
+ email: Optional[str] = None,
659
+ name: Optional[str] = None,
660
+ is_service_account: Optional[bool] = None,
661
+ ) -> List[OrganizationCollaborator]:
662
+ """Get organization collaborators."""
663
+ raise NotImplementedError
664
+
665
+ @abstractmethod
666
+ def delete_organization_collaborator(self, identity_id: str) -> None:
667
+ """Delete organization collaborator."""
668
+ raise NotImplementedError
669
+
670
+ @abstractmethod
671
+ def create_service_account(self, name: str) -> AnyscaleServiceAccount:
672
+ """Create a service account."""
644
673
  raise NotImplementedError
645
674
 
646
675
  @abstractmethod
@@ -16,6 +16,7 @@ from anyscale.cli_logger import BlockLogger
16
16
  from anyscale.client.openapi_client.models import (
17
17
  AdminCreatedUser,
18
18
  AdminCreateUser,
19
+ AnyscaleServiceAccount,
19
20
  Cloud,
20
21
  CloudProviders,
21
22
  ComputeTemplateConfig,
@@ -41,6 +42,7 @@ from anyscale.client.openapi_client.models import (
41
42
  ProductionJobStateTransition,
42
43
  Project,
43
44
  ResourceQuota,
45
+ ServerSessionToken,
44
46
  WorkspaceDataplaneProxiedArtifacts,
45
47
  )
46
48
  from anyscale.client.openapi_client.models.create_schedule import CreateSchedule
@@ -165,6 +167,8 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
165
167
  self._workspaces_env_vars: Dict[str, Dict[str, str]] = {}
166
168
  self._clusters_headnode_ip: Dict[str, str] = {}
167
169
  self._clusters_ssh_key: Dict[str, SessionSshKey] = {}
170
+ self._api_keys: Dict[str, List[str]] = defaultdict(list)
171
+ self._organization_collaborators: List[OrganizationCollaborator] = []
168
172
  self._organization_invitations: Dict[str, OrganizationInvitation] = {}
169
173
  self._resource_quotas: Dict[str, ResourceQuota] = {}
170
174
 
@@ -1139,6 +1143,20 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
1139
1143
 
1140
1144
  return filepath
1141
1145
 
1146
+ def create_api_key(
1147
+ self, duration: float, user_id: Optional[str] # noqa: ARG002
1148
+ ) -> ServerSessionToken:
1149
+ api_key = f"{uuid.uuid4()!s}"
1150
+ api_keys = self._api_keys.get(user_id or "usr_1", [])
1151
+ self._api_keys[user_id or "usr_1"] = api_keys + [api_key]
1152
+
1153
+ return ServerSessionToken(server_session_id=api_key)
1154
+
1155
+ def rotate_api_key(self, user_id: str) -> None:
1156
+ if user_id not in self._api_keys:
1157
+ raise ValueError(f"User '{user_id}' not found.")
1158
+ self._api_keys[user_id] = [f"{uuid.uuid4()!s}"]
1159
+
1142
1160
  def admin_batch_create_users(
1143
1161
  self, admin_create_users: List[AdminCreateUser]
1144
1162
  ) -> List[AdminCreatedUser]:
@@ -1195,14 +1213,59 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
1195
1213
 
1196
1214
  return self._organization_invitations.pop(email)
1197
1215
 
1198
- def get_organization_collaborator(self, email):
1199
- return OrganizationCollaborator(
1200
- id="identity_1",
1216
+ def get_organization_collaborators(
1217
+ self,
1218
+ email: Optional[str] = None,
1219
+ name: Optional[str] = None,
1220
+ is_service_account: Optional[bool] = None, # noqa: ARG002
1221
+ ) -> List[OrganizationCollaborator]:
1222
+ # Since organization collaborator doesn't include whether it's a service account or not, we'll just return all
1223
+ results = []
1224
+ for organization_collaborator in self._organization_collaborators:
1225
+ if email and organization_collaborator.email != email:
1226
+ continue
1227
+ if name and organization_collaborator.name != name:
1228
+ continue
1229
+
1230
+ results.append(organization_collaborator)
1231
+
1232
+ return results
1233
+
1234
+ def delete_organization_collaborator(self, identity_id: str) -> None:
1235
+ for organization_collaborator in self._organization_collaborators:
1236
+ if organization_collaborator.id == identity_id:
1237
+ self._organization_collaborators.remove(organization_collaborator)
1238
+ return
1239
+
1240
+ raise ValueError(
1241
+ f"Organization collaborator with id '{identity_id}' not found."
1242
+ )
1243
+
1244
+ def create_service_account(self, name) -> AnyscaleServiceAccount:
1245
+ for organization_collaborator in self._organization_collaborators:
1246
+ if organization_collaborator.name == name:
1247
+ raise ValueError(f"Service account with name '{name}' already exists.")
1248
+
1249
+ identity_id = f"id_{uuid.uuid4()!s}"
1250
+ user_id = f"usr_{uuid.uuid4()!s}"
1251
+ organization_collaborator = OrganizationCollaborator(
1201
1252
  permission_level=OrganizationPermissionLevel.COLLABORATOR,
1202
- name="Test User",
1253
+ id=identity_id,
1254
+ name=name,
1255
+ email=f"{name}@service-account.com",
1256
+ user_id=user_id,
1203
1257
  created_at=datetime.utcnow(),
1204
- email=email,
1205
- user_id="usr_1",
1258
+ )
1259
+
1260
+ self._organization_collaborators.append((organization_collaborator))
1261
+
1262
+ return AnyscaleServiceAccount(
1263
+ user_id=organization_collaborator.user_id,
1264
+ name=organization_collaborator.name,
1265
+ organization_id="org_1",
1266
+ email=organization_collaborator.email,
1267
+ permission_level=organization_collaborator.permission_level,
1268
+ created_at=organization_collaborator.created_at,
1206
1269
  )
1207
1270
 
1208
1271
  def create_resource_quota(
@@ -76,6 +76,7 @@ from anyscale.service.models import (
76
76
  ServiceVersionStatus,
77
77
  TracingConfig,
78
78
  )
79
+ from anyscale.service_account.models import OrganizationPermissionLevel, ServiceAccount
79
80
  from anyscale.user.models import AdminCreatedUser, AdminCreateUser
80
81
  from anyscale.workspace.models import WorkspaceConfig
81
82
 
@@ -356,8 +357,14 @@ ALL_MODULES = [
356
357
  service_account_commands.rotate_api_keys,
357
358
  ],
358
359
  sdk_prefix="anyscale.service_account",
359
- sdk_commands=[],
360
- models=[],
360
+ sdk_commands=[
361
+ anyscale.service_account.create,
362
+ anyscale.service_account.create_api_key,
363
+ anyscale.service_account.list,
364
+ anyscale.service_account.delete,
365
+ anyscale.service_account.rotate_api_keys,
366
+ ],
367
+ models=[ServiceAccount, OrganizationPermissionLevel],
361
368
  ),
362
369
  Module(
363
370
  title="Image",
@@ -949,6 +949,18 @@ Returns [ProductionjobResponse](./models.md#productionjobresponse)
949
949
 
950
950
  ## Services
951
951
 
952
+ ### archive_service
953
+
954
+ Archives a Service. It is a no-op if already archived.
955
+
956
+ Parameters
957
+
958
+ Name | Type | Description | Notes
959
+ ------------- | ------------- | ------------- | -------------
960
+ `service_id` | str| | Defaults to null
961
+
962
+ Returns void (empty response body)
963
+
952
964
  ### get_service
953
965
 
954
966
  Get a Service
@@ -972,6 +984,7 @@ Name | Type | Description | Notes
972
984
  `project_id` | optional str| project_id to filter by | Defaults to null
973
985
  `name` | optional str| name to filter by | Defaults to null
974
986
  `state_filter` | [List[ServiceEventCurrentState]](./models.md#serviceeventcurrentstate)| A list of Service states to filter by | Defaults to []
987
+ `archive_status` | [ArchiveStatus](./models.md#archivestatus)| The archive status to filter by. Defaults to unarchived. | Defaults to null
975
988
  `creator_id` | optional str| creator_id to filter by | Defaults to null
976
989
  `cloud_id` | optional str| cloud_id to filter by | Defaults to null
977
990
  `sort_field` | [ServiceSortField](./models.md#servicesortfield)| If absent, the default sorting order is 1. status (active first).2. Last updated at (desc). 3. Name (asc). | Defaults to null