anyscale 0.26.61__py3-none-any.whl → 0.26.63__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 (42) hide show
  1. anyscale/_private/anyscale_client/anyscale_client.py +4 -1
  2. anyscale/_private/docgen/__main__.py +0 -2
  3. anyscale/_private/docgen/models.md +2 -0
  4. anyscale/client/README.md +17 -3
  5. anyscale/client/openapi_client/__init__.py +10 -3
  6. anyscale/client/openapi_client/api/default_api.py +3229 -2400
  7. anyscale/client/openapi_client/models/__init__.py +10 -3
  8. anyscale/client/openapi_client/models/api_key_info.py +280 -0
  9. anyscale/client/openapi_client/models/api_key_parameters.py +29 -3
  10. anyscale/client/openapi_client/models/{aggregatedinstanceusagecsv_list_response.py → apikeyinfo_list_response.py} +15 -15
  11. anyscale/client/openapi_client/models/compute_node_type.py +29 -1
  12. anyscale/client/openapi_client/models/gpu_usage.py +236 -0
  13. anyscale/client/openapi_client/models/node_metrics.py +404 -0
  14. anyscale/client/openapi_client/models/node_metrics_response.py +123 -0
  15. anyscale/client/openapi_client/models/nodemetricsresponse_response.py +121 -0
  16. anyscale/client/openapi_client/models/operator_metrics.py +27 -1
  17. anyscale/client/openapi_client/models/revoke_api_keys_request.py +123 -0
  18. anyscale/client/openapi_client/models/revoke_api_keys_response.py +202 -0
  19. anyscale/client/openapi_client/models/revokeapikeysresponse_response.py +121 -0
  20. anyscale/client/openapi_client/models/{cloud_hosting_type.py → task_summary_config.py} +33 -13
  21. anyscale/client/openapi_client/models/task_table_config.py +29 -3
  22. anyscale/client/openapi_client/models/task_table_row.py +29 -3
  23. anyscale/client/openapi_client/models/worker_node_type.py +29 -1
  24. anyscale/commands/cloud_commands.py +146 -61
  25. anyscale/commands/command_examples.py +12 -0
  26. anyscale/commands/service_account_commands.py +0 -21
  27. anyscale/compute_config/_private/compute_config_sdk.py +4 -0
  28. anyscale/compute_config/models.py +24 -0
  29. anyscale/controllers/cloud_controller.py +94 -20
  30. anyscale/sdk/anyscale_client/models/compute_node_type.py +29 -1
  31. anyscale/sdk/anyscale_client/models/worker_node_type.py +29 -1
  32. anyscale/service_account/_private/service_account_sdk.py +10 -1
  33. anyscale/version.py +1 -1
  34. anyscale/workspace/commands.py +23 -114
  35. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/METADATA +1 -1
  36. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/RECORD +41 -34
  37. anyscale/client/openapi_client/models/aggregated_instance_usage_csv.py +0 -889
  38. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/WHEEL +0 -0
  39. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/entry_points.txt +0 -0
  40. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/licenses/LICENSE +0 -0
  41. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/licenses/NOTICE +0 -0
  42. {anyscale-0.26.61.dist-info → anyscale-0.26.63.dist-info}/top_level.txt +0 -0
@@ -629,6 +629,18 @@ Output
629
629
  --enable-system-cluster is specified. [...] Are you sure you want to enable system cluster? [y/N]: y
630
630
  Output
631
631
  (anyscale +11.4s) Successfully enabled system cluster for cloud cloud_id
632
+
633
+ $ anyscale cloud config update --cloud-id cloud_id --spec-file iam.yaml
634
+ Output
635
+ (anyscale +2.1s) Successfully updated cloud configuration for cloud my-cloud (resource: cldrsrc_xyz123)
636
+
637
+ $ anyscale cloud config update --cloud-id cloud_id --resource shared-usw2 --spec-file iam.yaml
638
+ Output
639
+ (anyscale +2.1s) Successfully updated cloud configuration for cloud my-cloud (resource: cldrsrc_abc456)
640
+
641
+ $ anyscale cloud config update --cloud-id cloud_id --cloud-resource-id cldrsrc_xyz123 --spec-file iam.yaml
642
+ Output
643
+ (anyscale +2.1s) Successfully updated cloud configuration for cloud my-cloud (resource: cldrsrc_xyz123)
632
644
  """
633
645
 
634
646
  CLOUD_TERMINATE_SYSTEM_CLUSTER_EXAMPLE = """\
@@ -106,24 +106,3 @@ def delete(email: Optional[str], name: Optional[str]) -> None:
106
106
  log.info(f"Service account {email or name} deleted successfully.")
107
107
  except ValueError as e:
108
108
  log.error(f"Error deleting service account: {e}")
109
-
110
-
111
- @service_account_cli.command(
112
- name="rotate-api-keys", help="Rotate all api keys of a service account."
113
- )
114
- @click.option(
115
- "--email", help="Rotate API keys for the service account with this email.", type=str
116
- )
117
- @click.option(
118
- "--name", help="Rotate API keys for the service account with this name.", type=str
119
- )
120
- def rotate_api_keys(email: Optional[str], name: Optional[str]) -> None:
121
- try:
122
- api_key = anyscale.service_account.rotate_api_keys(email, name)
123
-
124
- log.info(
125
- f"\nAll API keys for service account {email or name} rotated successfully."
126
- )
127
- _print_new_api_key(api_key)
128
- except ValueError as e:
129
- log.error(f"Error rotating API keys: {e}")
@@ -84,6 +84,7 @@ class PrivateComputeConfigSDK(BaseSDK):
84
84
  resources=self._convert_resource_dict_to_api_model(config.resources)
85
85
  if config.resources is not None or schedulable_by_default
86
86
  else UNSCHEDULABLE_RESOURCES,
87
+ labels=config.labels,
87
88
  flags=flags or None,
88
89
  advanced_configurations_json=config.advanced_instance_config or None,
89
90
  )
@@ -110,6 +111,7 @@ class PrivateComputeConfigSDK(BaseSDK):
110
111
  name=config.name,
111
112
  instance_type=config.instance_type,
112
113
  resources=self._convert_resource_dict_to_api_model(config.resources),
114
+ labels=config.labels,
113
115
  min_workers=config.min_nodes,
114
116
  max_workers=config.max_nodes,
115
117
  use_spot=config.market_type
@@ -318,6 +320,7 @@ class PrivateComputeConfigSDK(BaseSDK):
318
320
  return HeadNodeConfig(
319
321
  instance_type=api_model.instance_type,
320
322
  resources=self._convert_api_model_to_resource_dict(api_model.resources),
323
+ labels=api_model.labels,
321
324
  advanced_instance_config=self._convert_api_model_to_advanced_instance_config(
322
325
  api_model,
323
326
  ),
@@ -365,6 +368,7 @@ class PrivateComputeConfigSDK(BaseSDK):
365
368
  resources=self._convert_api_model_to_resource_dict(
366
369
  api_model.resources
367
370
  ),
371
+ labels=api_model.labels,
368
372
  advanced_instance_config=self._convert_api_model_to_advanced_instance_config(
369
373
  api_model,
370
374
  ),
@@ -6,6 +6,7 @@ from anyscale._private.models import ModelBase, ModelEnum
6
6
 
7
7
 
8
8
  ResourceDict = Dict[str, float]
9
+ LabelDict = Dict[str, str]
9
10
  AdvancedInstanceConfigDict = Dict[str, Any]
10
11
 
11
12
 
@@ -30,6 +31,18 @@ def _validate_resource_dict(r: Optional[ResourceDict], *, field_name: str):
30
31
  )
31
32
 
32
33
 
34
+ def _validate_label_dict(labels: Optional[LabelDict]):
35
+ if labels is None:
36
+ return
37
+
38
+ # Convert any non-string keys/values to strings to ensure compatibility
39
+ for k, v in labels.items():
40
+ if not isinstance(k, str):
41
+ raise TypeError(f"'labels' keys must be strings, but got: {k}")
42
+ if not isinstance(v, str):
43
+ raise TypeError(f"'labels' values must be strings, but got: {v}")
44
+
45
+
33
46
  def _validate_advanced_instance_config_dict(c: Optional[AdvancedInstanceConfigDict]):
34
47
  if c is None:
35
48
  return
@@ -119,6 +132,17 @@ class _NodeConfig(ModelBase):
119
132
  def _validate_resources(self, resources: Optional[ResourceDict]):
120
133
  _validate_resource_dict(resources, field_name="resources")
121
134
 
135
+ labels: Optional[LabelDict] = field(
136
+ default=None,
137
+ repr=False,
138
+ metadata={
139
+ "docstring": "Labels to associate the node with for scheduling purposes. Defaults to the list of Ray & Anyscale default labels."
140
+ },
141
+ )
142
+
143
+ def _validate_labels(self, labels: Optional[LabelDict]):
144
+ _validate_label_dict(labels)
145
+
122
146
  advanced_instance_config: Optional[AdvancedInstanceConfigDict] = field(
123
147
  default=None,
124
148
  repr=False,
@@ -1929,18 +1929,94 @@ class CloudController(BaseController):
1929
1929
  f"Successfully removed resource {resource_name} from cloud {cloud_name}!"
1930
1930
  )
1931
1931
 
1932
+ def _resolve_cloud_resource_id(
1933
+ self,
1934
+ cloud_id: str,
1935
+ resource: Optional[str] = None,
1936
+ cloud_resource_id: Optional[str] = None,
1937
+ ) -> str:
1938
+ """
1939
+ Resolve cloud resource ID based on resolution order: id > name > primary.
1940
+
1941
+ Returns the resolved cloud_resource_id to use for API calls.
1942
+ """
1943
+ # If cloud_resource_id is provided, use it directly
1944
+ if cloud_resource_id:
1945
+ self.log.info(f"Using provided cloud resource ID: {cloud_resource_id}")
1946
+ return cloud_resource_id
1947
+
1948
+ # If resource name is provided, resolve by name
1949
+ if resource:
1950
+ # Get all cloud resources to resolve by name
1951
+ cloud_resources = self.api_client.get_cloud_deployments_api_v2_clouds_cloud_id_deployments_get(
1952
+ cloud_id=cloud_id
1953
+ ).results
1954
+
1955
+ if not cloud_resources:
1956
+ raise RuntimeError(f"No cloud resources found for cloud {cloud_id}")
1957
+
1958
+ # Find resources with matching name
1959
+ matching_resources = [r for r in cloud_resources if r.name == resource]
1960
+
1961
+ if not matching_resources:
1962
+ raise RuntimeError(
1963
+ f"No cloud resource found with name '{resource}' in cloud {cloud_id}"
1964
+ )
1965
+
1966
+ if len(matching_resources) > 1:
1967
+ raise RuntimeError(
1968
+ f"Multiple cloud resources found with name '{resource}'. "
1969
+ f"Please use --cloud-resource-id to specify which resource to use. "
1970
+ f"Available resource IDs: {[r.cloud_deployment_id for r in matching_resources]}"
1971
+ )
1972
+
1973
+ resolved_id = matching_resources[0].cloud_deployment_id
1974
+ self.log.info(f"Resolved resource name '{resource}' to ID: {resolved_id}")
1975
+ return resolved_id
1976
+
1977
+ # Default to primary resource (marked with is_default=True)
1978
+ cloud_resources = self.api_client.get_cloud_deployments_api_v2_clouds_cloud_id_deployments_get(
1979
+ cloud_id=cloud_id
1980
+ ).results
1981
+
1982
+ if not cloud_resources:
1983
+ raise RuntimeError(f"No cloud resources found for cloud {cloud_id}")
1984
+
1985
+ # Find primary resource (is_default=True)
1986
+ primary_resources = [r for r in cloud_resources if r.is_default]
1987
+
1988
+ if not primary_resources:
1989
+ raise RuntimeError(f"No primary cloud resource found for cloud {cloud_id}")
1990
+
1991
+ if len(primary_resources) > 1:
1992
+ raise RuntimeError(
1993
+ f"Multiple primary cloud resources found for cloud {cloud_id}"
1994
+ )
1995
+
1996
+ resolved_id = primary_resources[0].cloud_deployment_id
1997
+ self.log.info(f"Using primary cloud resource ID: {resolved_id}")
1998
+ return resolved_id
1999
+
1932
2000
  def get_cloud_config(
1933
- self, cloud_name: Optional[str] = None, cloud_id: Optional[str] = None,
2001
+ self,
2002
+ cloud_name: Optional[str] = None,
2003
+ cloud_id: Optional[str] = None,
2004
+ resource: Optional[str] = None,
2005
+ cloud_resource_id: Optional[str] = None,
1934
2006
  ) -> CloudDeploymentConfig:
1935
2007
  """Get a cloud's current JSON configuration."""
1936
2008
 
1937
- cloud_id, cloud_name = get_cloud_id_and_name(
2009
+ resolved_cloud_id, cloud_name = get_cloud_id_and_name(
1938
2010
  self.api_client, cloud_id, cloud_name
1939
2011
  )
1940
2012
 
1941
- # In the future we will expose cloud_deployment id as a parameter, for now, it's just a placeholder.
2013
+ # Resolve the cloud resource ID to use
2014
+ resolved_cloud_resource_id = self._resolve_cloud_resource_id(
2015
+ resolved_cloud_id, resource, cloud_resource_id
2016
+ )
2017
+
1942
2018
  config: CloudDeploymentConfig = self.api_client.get_cloud_deployment_config_api_v2_clouds_cloud_id_deployment_cloud_deployment_id_config_get(
1943
- cloud_id=cloud_id, cloud_deployment_id="default"
2019
+ cloud_id=resolved_cloud_id, cloud_deployment_id=resolved_cloud_resource_id
1944
2020
  ).result
1945
2021
 
1946
2022
  return config
@@ -1951,21 +2027,23 @@ class CloudController(BaseController):
1951
2027
  cloud_id: Optional[str] = None,
1952
2028
  enable_log_ingestion: Optional[bool] = None,
1953
2029
  spec_file: Optional[str] = None,
2030
+ resource: Optional[str] = None,
2031
+ cloud_resource_id: Optional[str] = None,
1954
2032
  ):
1955
2033
  """Update a cloud's configuration."""
1956
2034
  if enable_log_ingestion is None and spec_file is None:
1957
2035
  return
1958
2036
 
1959
- cloud_id, cloud_name = get_cloud_id_and_name(
2037
+ resolved_cloud_id, cloud_name = get_cloud_id_and_name(
1960
2038
  self.api_client, cloud_id, cloud_name
1961
2039
  )
1962
2040
  if enable_log_ingestion is not None:
1963
2041
  self._update_customer_aggregated_logs_config(
1964
- cloud_id=cloud_id, is_enabled=enable_log_ingestion, # type: ignore
2042
+ cloud_id=resolved_cloud_id, is_enabled=enable_log_ingestion, # type: ignore
1965
2043
  )
1966
2044
  self.log.info(
1967
2045
  f"Successfully updated log ingestion configuration for cloud, "
1968
- f"{cloud_id} to {enable_log_ingestion}"
2046
+ f"{resolved_cloud_id} to {enable_log_ingestion}"
1969
2047
  )
1970
2048
  elif spec_file is not None:
1971
2049
  path = pathlib.Path(spec_file)
@@ -1975,15 +2053,21 @@ class CloudController(BaseController):
1975
2053
  if not path.is_file():
1976
2054
  raise ValueError(f"File {spec_file} is not a file.")
1977
2055
 
2056
+ # Resolve the cloud resource ID to use
2057
+ resolved_cloud_resource_id = self._resolve_cloud_resource_id(
2058
+ resolved_cloud_id, resource, cloud_resource_id
2059
+ )
2060
+
1978
2061
  spec = yaml.safe_load(path.read_text())
1979
2062
  config = CloudDeploymentConfig(spec=spec)
1980
2063
  self.api_client.update_cloud_deployment_config_api_v2_clouds_cloud_id_deployment_cloud_deployment_id_config_put(
1981
- cloud_id=cloud_id,
1982
- cloud_deployment_id="default",
2064
+ cloud_id=resolved_cloud_id,
2065
+ cloud_deployment_id=resolved_cloud_resource_id,
1983
2066
  cloud_deployment_config=config,
1984
2067
  )
1985
2068
  self.log.info(
1986
- f"Successfully updated cloud configuration for cloud {cloud_name}"
2069
+ f"Successfully updated cloud configuration for cloud {cloud_name} "
2070
+ f"(resource: {resolved_cloud_resource_id})"
1987
2071
  )
1988
2072
 
1989
2073
  def set_default_cloud(
@@ -2693,7 +2777,6 @@ class CloudController(BaseController):
2693
2777
  provider=provider,
2694
2778
  cloud_deployment_id=cloud_resource_id,
2695
2779
  region=region if cloud_provider == CloudProviders.AZURE else None,
2696
- kubernetes_zones=kubernetes_zones,
2697
2780
  operator_iam_identity=anyscale_operator_iam_identity
2698
2781
  if cloud_provider == CloudProviders.AZURE
2699
2782
  else None,
@@ -2939,7 +3022,6 @@ class CloudController(BaseController):
2939
3022
  provider="aws",
2940
3023
  cloud_deployment_id=cloud_resource_id,
2941
3024
  region=region,
2942
- kubernetes_zones=kubernetes_zones,
2943
3025
  )
2944
3026
  self.log.info(
2945
3027
  f"Cloud registration complete! To install the Anyscale operator, run:\n\n{helm_command}"
@@ -3434,7 +3516,6 @@ class CloudController(BaseController):
3434
3516
  provider="gcp",
3435
3517
  cloud_deployment_id=cloud_resource_id,
3436
3518
  region=region,
3437
- kubernetes_zones=kubernetes_zones,
3438
3519
  operator_iam_identity=anyscale_service_account_email,
3439
3520
  )
3440
3521
  gcloud_command = self._generate_gcp_workload_identity_command(
@@ -4160,7 +4241,6 @@ class CloudController(BaseController):
4160
4241
  provider: str,
4161
4242
  cloud_deployment_id: str,
4162
4243
  region: Optional[str] = None,
4163
- kubernetes_zones: Optional[List[str]] = None,
4164
4244
  operator_iam_identity: Optional[str] = None,
4165
4245
  anyscale_cli_token: Optional[str] = None,
4166
4246
  ) -> str:
@@ -4171,7 +4251,6 @@ class CloudController(BaseController):
4171
4251
  provider: Cloud provider ('aws', 'gcp', 'azure', 'generic')
4172
4252
  cloud_deployment_id: The cloud deployment ID from registration
4173
4253
  region: Cloud region (optional for generic provider)
4174
- kubernetes_zones: Optional list of Kubernetes zones
4175
4254
  operator_iam_identity: IAM identity for the operator (GCP service account email, Azure client ID)
4176
4255
  anyscale_cli_token: CLI token (required for Azure and generic providers)
4177
4256
 
@@ -4226,11 +4305,6 @@ class CloudController(BaseController):
4226
4305
  ]
4227
4306
  )
4228
4307
 
4229
- # Add zones if provided (mainly for K8s deployments)
4230
- if kubernetes_zones:
4231
- zones_str = ",".join(kubernetes_zones)
4232
- command_parts.append(f" --set-string zones={zones_str}")
4233
-
4234
4308
  return " \\\n".join(command_parts)
4235
4309
 
4236
4310
  def _generate_gcp_workload_identity_command(
@@ -36,6 +36,7 @@ class ComputeNodeType(object):
36
36
  'name': 'str',
37
37
  'instance_type': 'str',
38
38
  'resources': 'Resources',
39
+ 'labels': 'dict(str, str)',
39
40
  'aws_advanced_configurations_json': 'object',
40
41
  'gcp_advanced_configurations_json': 'object',
41
42
  'advanced_configurations_json': 'object',
@@ -46,13 +47,14 @@ class ComputeNodeType(object):
46
47
  'name': 'name',
47
48
  'instance_type': 'instance_type',
48
49
  'resources': 'resources',
50
+ 'labels': 'labels',
49
51
  'aws_advanced_configurations_json': 'aws_advanced_configurations_json',
50
52
  'gcp_advanced_configurations_json': 'gcp_advanced_configurations_json',
51
53
  'advanced_configurations_json': 'advanced_configurations_json',
52
54
  'flags': 'flags'
53
55
  }
54
56
 
55
- def __init__(self, name=None, instance_type=None, resources=None, aws_advanced_configurations_json=None, gcp_advanced_configurations_json=None, advanced_configurations_json=None, flags=None, local_vars_configuration=None): # noqa: E501
57
+ def __init__(self, name=None, instance_type=None, resources=None, labels=None, aws_advanced_configurations_json=None, gcp_advanced_configurations_json=None, advanced_configurations_json=None, flags=None, local_vars_configuration=None): # noqa: E501
56
58
  """ComputeNodeType - a model defined in OpenAPI""" # noqa: E501
57
59
  if local_vars_configuration is None:
58
60
  local_vars_configuration = Configuration()
@@ -61,6 +63,7 @@ class ComputeNodeType(object):
61
63
  self._name = None
62
64
  self._instance_type = None
63
65
  self._resources = None
66
+ self._labels = None
64
67
  self._aws_advanced_configurations_json = None
65
68
  self._gcp_advanced_configurations_json = None
66
69
  self._advanced_configurations_json = None
@@ -71,6 +74,8 @@ class ComputeNodeType(object):
71
74
  self.instance_type = instance_type
72
75
  if resources is not None:
73
76
  self.resources = resources
77
+ if labels is not None:
78
+ self.labels = labels
74
79
  if aws_advanced_configurations_json is not None:
75
80
  self.aws_advanced_configurations_json = aws_advanced_configurations_json
76
81
  if gcp_advanced_configurations_json is not None:
@@ -153,6 +158,29 @@ class ComputeNodeType(object):
153
158
 
154
159
  self._resources = resources
155
160
 
161
+ @property
162
+ def labels(self):
163
+ """Gets the labels of this ComputeNodeType. # noqa: E501
164
+
165
+ Labels to associate the node with for scheduling purposes. Defaults to the list of Ray & Anyscale default labels. # noqa: E501
166
+
167
+ :return: The labels of this ComputeNodeType. # noqa: E501
168
+ :rtype: dict(str, str)
169
+ """
170
+ return self._labels
171
+
172
+ @labels.setter
173
+ def labels(self, labels):
174
+ """Sets the labels of this ComputeNodeType.
175
+
176
+ Labels to associate the node with for scheduling purposes. Defaults to the list of Ray & Anyscale default labels. # noqa: E501
177
+
178
+ :param labels: The labels of this ComputeNodeType. # noqa: E501
179
+ :type: dict(str, str)
180
+ """
181
+
182
+ self._labels = labels
183
+
156
184
  @property
157
185
  def aws_advanced_configurations_json(self):
158
186
  """Gets the aws_advanced_configurations_json of this ComputeNodeType. # noqa: E501
@@ -36,6 +36,7 @@ class WorkerNodeType(object):
36
36
  'name': 'str',
37
37
  'instance_type': 'str',
38
38
  'resources': 'Resources',
39
+ 'labels': 'dict(str, str)',
39
40
  'aws_advanced_configurations_json': 'object',
40
41
  'gcp_advanced_configurations_json': 'object',
41
42
  'advanced_configurations_json': 'object',
@@ -50,6 +51,7 @@ class WorkerNodeType(object):
50
51
  'name': 'name',
51
52
  'instance_type': 'instance_type',
52
53
  'resources': 'resources',
54
+ 'labels': 'labels',
53
55
  'aws_advanced_configurations_json': 'aws_advanced_configurations_json',
54
56
  'gcp_advanced_configurations_json': 'gcp_advanced_configurations_json',
55
57
  'advanced_configurations_json': 'advanced_configurations_json',
@@ -60,7 +62,7 @@ class WorkerNodeType(object):
60
62
  'fallback_to_ondemand': 'fallback_to_ondemand'
61
63
  }
62
64
 
63
- def __init__(self, name=None, instance_type=None, resources=None, aws_advanced_configurations_json=None, gcp_advanced_configurations_json=None, advanced_configurations_json=None, flags=None, min_workers=None, max_workers=None, use_spot=False, fallback_to_ondemand=False, local_vars_configuration=None): # noqa: E501
65
+ def __init__(self, name=None, instance_type=None, resources=None, labels=None, aws_advanced_configurations_json=None, gcp_advanced_configurations_json=None, advanced_configurations_json=None, flags=None, min_workers=None, max_workers=None, use_spot=False, fallback_to_ondemand=False, local_vars_configuration=None): # noqa: E501
64
66
  """WorkerNodeType - a model defined in OpenAPI""" # noqa: E501
65
67
  if local_vars_configuration is None:
66
68
  local_vars_configuration = Configuration()
@@ -69,6 +71,7 @@ class WorkerNodeType(object):
69
71
  self._name = None
70
72
  self._instance_type = None
71
73
  self._resources = None
74
+ self._labels = None
72
75
  self._aws_advanced_configurations_json = None
73
76
  self._gcp_advanced_configurations_json = None
74
77
  self._advanced_configurations_json = None
@@ -83,6 +86,8 @@ class WorkerNodeType(object):
83
86
  self.instance_type = instance_type
84
87
  if resources is not None:
85
88
  self.resources = resources
89
+ if labels is not None:
90
+ self.labels = labels
86
91
  if aws_advanced_configurations_json is not None:
87
92
  self.aws_advanced_configurations_json = aws_advanced_configurations_json
88
93
  if gcp_advanced_configurations_json is not None:
@@ -173,6 +178,29 @@ class WorkerNodeType(object):
173
178
 
174
179
  self._resources = resources
175
180
 
181
+ @property
182
+ def labels(self):
183
+ """Gets the labels of this WorkerNodeType. # noqa: E501
184
+
185
+ Labels to associate the node with for scheduling purposes. Defaults to the list of Ray & Anyscale default labels. # noqa: E501
186
+
187
+ :return: The labels of this WorkerNodeType. # noqa: E501
188
+ :rtype: dict(str, str)
189
+ """
190
+ return self._labels
191
+
192
+ @labels.setter
193
+ def labels(self, labels):
194
+ """Sets the labels of this WorkerNodeType.
195
+
196
+ Labels to associate the node with for scheduling purposes. Defaults to the list of Ray & Anyscale default labels. # noqa: E501
197
+
198
+ :param labels: The labels of this WorkerNodeType. # noqa: E501
199
+ :type: dict(str, str)
200
+ """
201
+
202
+ self._labels = labels
203
+
176
204
  @property
177
205
  def aws_advanced_configurations_json(self):
178
206
  """Gets the aws_advanced_configurations_json of this WorkerNodeType. # noqa: E501
@@ -33,8 +33,11 @@ class PrivateServiceAccountSDK(BaseSDK):
33
33
  raise ValueError(f"No service account {identifier} found.")
34
34
 
35
35
  if len(service_accounts) > 1:
36
+ names = [sa.name for sa in service_accounts]
37
+ emails = [sa.email for sa in service_accounts]
36
38
  raise ValueError(
37
- f"Internal server error when fetching service account {identifier}. Please contact support."
39
+ f"Found {len(service_accounts)} service accounts matching '{identifier}'. "
40
+ f"Names: {names}, Emails: {emails}. This should not happen - please contact support."
38
41
  )
39
42
 
40
43
  def _get_service_account(
@@ -44,6 +47,12 @@ class PrivateServiceAccountSDK(BaseSDK):
44
47
  service_accounts = self.client.get_organization_collaborators(
45
48
  email=email, name=name, is_service_account=True
46
49
  )
50
+
51
+ if name is not None:
52
+ service_accounts = [sa for sa in service_accounts if sa.name == name]
53
+ if email is not None:
54
+ service_accounts = [sa for sa in service_accounts if sa.email == email]
55
+
47
56
  self._validate_exactly_one_service_account_per_email_or_name(
48
57
  service_accounts, identifier
49
58
  )
anyscale/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.26.61"
1
+ __version__ = "0.26.63"