anyscale 0.25.2__py3-none-any.whl → 0.25.4__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/anyscale_client.py +5 -0
- anyscale/_private/anyscale_client/common.py +5 -0
- anyscale/_private/anyscale_client/fake_anyscale_client.py +11 -0
- anyscale/_private/workload/workload_config.py +20 -6
- anyscale/client/README.md +6 -0
- anyscale/client/openapi_client/__init__.py +4 -0
- anyscale/client/openapi_client/api/default_api.py +272 -2
- anyscale/client/openapi_client/models/__init__.py +4 -0
- anyscale/client/openapi_client/models/cluster_event_source.py +105 -0
- anyscale/client/openapi_client/models/clusterevent_list_response.py +147 -0
- anyscale/client/openapi_client/models/create_notification_channel_record.py +29 -3
- anyscale/client/openapi_client/models/notification_channel_slack_config.py +121 -0
- anyscale/client/openapi_client/models/update_cloud_collaborator.py +121 -0
- anyscale/commands/cloud_commands.py +24 -4
- anyscale/commands/command_examples.py +4 -0
- anyscale/commands/service_commands.py +60 -0
- anyscale/controllers/cloud_controller.py +29 -4
- anyscale/service/__init__.py +21 -0
- anyscale/service/_private/service_sdk.py +13 -0
- anyscale/service/commands.py +35 -0
- anyscale/shared_anyscale_utils/utils/id_gen.py +1 -0
- anyscale/version.py +1 -1
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/METADATA +1 -1
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/RECORD +29 -25
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/LICENSE +0 -0
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/NOTICE +0 -0
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/WHEEL +0 -0
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/entry_points.txt +0 -0
- {anyscale-0.25.2.dist-info → anyscale-0.25.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
"""
|
4
|
+
Managed Ray API
|
5
|
+
|
6
|
+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501
|
7
|
+
|
8
|
+
The version of the OpenAPI document: 0.1.0
|
9
|
+
Generated by: https://openapi-generator.tech
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
import pprint
|
14
|
+
import re # noqa: F401
|
15
|
+
|
16
|
+
import six
|
17
|
+
|
18
|
+
from openapi_client.configuration import Configuration
|
19
|
+
|
20
|
+
|
21
|
+
class ClustereventListResponse(object):
|
22
|
+
"""NOTE: This class is auto generated by OpenAPI Generator.
|
23
|
+
Ref: https://openapi-generator.tech
|
24
|
+
|
25
|
+
Do not edit the class manually.
|
26
|
+
"""
|
27
|
+
|
28
|
+
"""
|
29
|
+
Attributes:
|
30
|
+
openapi_types (dict): The key is attribute name
|
31
|
+
and the value is attribute type.
|
32
|
+
attribute_map (dict): The key is attribute name
|
33
|
+
and the value is json key in definition.
|
34
|
+
"""
|
35
|
+
openapi_types = {
|
36
|
+
'results': 'list[ClusterEvent]',
|
37
|
+
'metadata': 'ListResponseMetadata'
|
38
|
+
}
|
39
|
+
|
40
|
+
attribute_map = {
|
41
|
+
'results': 'results',
|
42
|
+
'metadata': 'metadata'
|
43
|
+
}
|
44
|
+
|
45
|
+
def __init__(self, results=None, metadata=None, local_vars_configuration=None): # noqa: E501
|
46
|
+
"""ClustereventListResponse - a model defined in OpenAPI""" # noqa: E501
|
47
|
+
if local_vars_configuration is None:
|
48
|
+
local_vars_configuration = Configuration()
|
49
|
+
self.local_vars_configuration = local_vars_configuration
|
50
|
+
|
51
|
+
self._results = None
|
52
|
+
self._metadata = None
|
53
|
+
self.discriminator = None
|
54
|
+
|
55
|
+
self.results = results
|
56
|
+
if metadata is not None:
|
57
|
+
self.metadata = metadata
|
58
|
+
|
59
|
+
@property
|
60
|
+
def results(self):
|
61
|
+
"""Gets the results of this ClustereventListResponse. # noqa: E501
|
62
|
+
|
63
|
+
|
64
|
+
:return: The results of this ClustereventListResponse. # noqa: E501
|
65
|
+
:rtype: list[ClusterEvent]
|
66
|
+
"""
|
67
|
+
return self._results
|
68
|
+
|
69
|
+
@results.setter
|
70
|
+
def results(self, results):
|
71
|
+
"""Sets the results of this ClustereventListResponse.
|
72
|
+
|
73
|
+
|
74
|
+
:param results: The results of this ClustereventListResponse. # noqa: E501
|
75
|
+
:type: list[ClusterEvent]
|
76
|
+
"""
|
77
|
+
if self.local_vars_configuration.client_side_validation and results is None: # noqa: E501
|
78
|
+
raise ValueError("Invalid value for `results`, must not be `None`") # noqa: E501
|
79
|
+
|
80
|
+
self._results = results
|
81
|
+
|
82
|
+
@property
|
83
|
+
def metadata(self):
|
84
|
+
"""Gets the metadata of this ClustereventListResponse. # noqa: E501
|
85
|
+
|
86
|
+
|
87
|
+
:return: The metadata of this ClustereventListResponse. # noqa: E501
|
88
|
+
:rtype: ListResponseMetadata
|
89
|
+
"""
|
90
|
+
return self._metadata
|
91
|
+
|
92
|
+
@metadata.setter
|
93
|
+
def metadata(self, metadata):
|
94
|
+
"""Sets the metadata of this ClustereventListResponse.
|
95
|
+
|
96
|
+
|
97
|
+
:param metadata: The metadata of this ClustereventListResponse. # noqa: E501
|
98
|
+
:type: ListResponseMetadata
|
99
|
+
"""
|
100
|
+
|
101
|
+
self._metadata = metadata
|
102
|
+
|
103
|
+
def to_dict(self):
|
104
|
+
"""Returns the model properties as a dict"""
|
105
|
+
result = {}
|
106
|
+
|
107
|
+
for attr, _ in six.iteritems(self.openapi_types):
|
108
|
+
value = getattr(self, attr)
|
109
|
+
if isinstance(value, list):
|
110
|
+
result[attr] = list(map(
|
111
|
+
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
112
|
+
value
|
113
|
+
))
|
114
|
+
elif hasattr(value, "to_dict"):
|
115
|
+
result[attr] = value.to_dict()
|
116
|
+
elif isinstance(value, dict):
|
117
|
+
result[attr] = dict(map(
|
118
|
+
lambda item: (item[0], item[1].to_dict())
|
119
|
+
if hasattr(item[1], "to_dict") else item,
|
120
|
+
value.items()
|
121
|
+
))
|
122
|
+
else:
|
123
|
+
result[attr] = value
|
124
|
+
|
125
|
+
return result
|
126
|
+
|
127
|
+
def to_str(self):
|
128
|
+
"""Returns the string representation of the model"""
|
129
|
+
return pprint.pformat(self.to_dict())
|
130
|
+
|
131
|
+
def __repr__(self):
|
132
|
+
"""For `print` and `pprint`"""
|
133
|
+
return self.to_str()
|
134
|
+
|
135
|
+
def __eq__(self, other):
|
136
|
+
"""Returns true if both objects are equal"""
|
137
|
+
if not isinstance(other, ClustereventListResponse):
|
138
|
+
return False
|
139
|
+
|
140
|
+
return self.to_dict() == other.to_dict()
|
141
|
+
|
142
|
+
def __ne__(self, other):
|
143
|
+
"""Returns true if both objects are not equal"""
|
144
|
+
if not isinstance(other, ClustereventListResponse):
|
145
|
+
return True
|
146
|
+
|
147
|
+
return self.to_dict() != other.to_dict()
|
@@ -34,15 +34,17 @@ class CreateNotificationChannelRecord(object):
|
|
34
34
|
"""
|
35
35
|
openapi_types = {
|
36
36
|
'email_config': 'NotificationChannelEmailConfig',
|
37
|
-
'webhook_config': 'NotificationChannelWebhookConfig'
|
37
|
+
'webhook_config': 'NotificationChannelWebhookConfig',
|
38
|
+
'slack_config': 'NotificationChannelSlackConfig'
|
38
39
|
}
|
39
40
|
|
40
41
|
attribute_map = {
|
41
42
|
'email_config': 'email_config',
|
42
|
-
'webhook_config': 'webhook_config'
|
43
|
+
'webhook_config': 'webhook_config',
|
44
|
+
'slack_config': 'slack_config'
|
43
45
|
}
|
44
46
|
|
45
|
-
def __init__(self, email_config=None, webhook_config=None, local_vars_configuration=None): # noqa: E501
|
47
|
+
def __init__(self, email_config=None, webhook_config=None, slack_config=None, local_vars_configuration=None): # noqa: E501
|
46
48
|
"""CreateNotificationChannelRecord - a model defined in OpenAPI""" # noqa: E501
|
47
49
|
if local_vars_configuration is None:
|
48
50
|
local_vars_configuration = Configuration()
|
@@ -50,12 +52,15 @@ class CreateNotificationChannelRecord(object):
|
|
50
52
|
|
51
53
|
self._email_config = None
|
52
54
|
self._webhook_config = None
|
55
|
+
self._slack_config = None
|
53
56
|
self.discriminator = None
|
54
57
|
|
55
58
|
if email_config is not None:
|
56
59
|
self.email_config = email_config
|
57
60
|
if webhook_config is not None:
|
58
61
|
self.webhook_config = webhook_config
|
62
|
+
if slack_config is not None:
|
63
|
+
self.slack_config = slack_config
|
59
64
|
|
60
65
|
@property
|
61
66
|
def email_config(self):
|
@@ -99,6 +104,27 @@ class CreateNotificationChannelRecord(object):
|
|
99
104
|
|
100
105
|
self._webhook_config = webhook_config
|
101
106
|
|
107
|
+
@property
|
108
|
+
def slack_config(self):
|
109
|
+
"""Gets the slack_config of this CreateNotificationChannelRecord. # noqa: E501
|
110
|
+
|
111
|
+
|
112
|
+
:return: The slack_config of this CreateNotificationChannelRecord. # noqa: E501
|
113
|
+
:rtype: NotificationChannelSlackConfig
|
114
|
+
"""
|
115
|
+
return self._slack_config
|
116
|
+
|
117
|
+
@slack_config.setter
|
118
|
+
def slack_config(self, slack_config):
|
119
|
+
"""Sets the slack_config of this CreateNotificationChannelRecord.
|
120
|
+
|
121
|
+
|
122
|
+
:param slack_config: The slack_config of this CreateNotificationChannelRecord. # noqa: E501
|
123
|
+
:type: NotificationChannelSlackConfig
|
124
|
+
"""
|
125
|
+
|
126
|
+
self._slack_config = slack_config
|
127
|
+
|
102
128
|
def to_dict(self):
|
103
129
|
"""Returns the model properties as a dict"""
|
104
130
|
result = {}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
"""
|
4
|
+
Managed Ray API
|
5
|
+
|
6
|
+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501
|
7
|
+
|
8
|
+
The version of the OpenAPI document: 0.1.0
|
9
|
+
Generated by: https://openapi-generator.tech
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
import pprint
|
14
|
+
import re # noqa: F401
|
15
|
+
|
16
|
+
import six
|
17
|
+
|
18
|
+
from openapi_client.configuration import Configuration
|
19
|
+
|
20
|
+
|
21
|
+
class NotificationChannelSlackConfig(object):
|
22
|
+
"""NOTE: This class is auto generated by OpenAPI Generator.
|
23
|
+
Ref: https://openapi-generator.tech
|
24
|
+
|
25
|
+
Do not edit the class manually.
|
26
|
+
"""
|
27
|
+
|
28
|
+
"""
|
29
|
+
Attributes:
|
30
|
+
openapi_types (dict): The key is attribute name
|
31
|
+
and the value is attribute type.
|
32
|
+
attribute_map (dict): The key is attribute name
|
33
|
+
and the value is json key in definition.
|
34
|
+
"""
|
35
|
+
openapi_types = {
|
36
|
+
'slack_urls': 'list[str]'
|
37
|
+
}
|
38
|
+
|
39
|
+
attribute_map = {
|
40
|
+
'slack_urls': 'slack_urls'
|
41
|
+
}
|
42
|
+
|
43
|
+
def __init__(self, slack_urls=None, local_vars_configuration=None): # noqa: E501
|
44
|
+
"""NotificationChannelSlackConfig - a model defined in OpenAPI""" # noqa: E501
|
45
|
+
if local_vars_configuration is None:
|
46
|
+
local_vars_configuration = Configuration()
|
47
|
+
self.local_vars_configuration = local_vars_configuration
|
48
|
+
|
49
|
+
self._slack_urls = None
|
50
|
+
self.discriminator = None
|
51
|
+
|
52
|
+
self.slack_urls = slack_urls
|
53
|
+
|
54
|
+
@property
|
55
|
+
def slack_urls(self):
|
56
|
+
"""Gets the slack_urls of this NotificationChannelSlackConfig. # noqa: E501
|
57
|
+
|
58
|
+
|
59
|
+
:return: The slack_urls of this NotificationChannelSlackConfig. # noqa: E501
|
60
|
+
:rtype: list[str]
|
61
|
+
"""
|
62
|
+
return self._slack_urls
|
63
|
+
|
64
|
+
@slack_urls.setter
|
65
|
+
def slack_urls(self, slack_urls):
|
66
|
+
"""Sets the slack_urls of this NotificationChannelSlackConfig.
|
67
|
+
|
68
|
+
|
69
|
+
:param slack_urls: The slack_urls of this NotificationChannelSlackConfig. # noqa: E501
|
70
|
+
:type: list[str]
|
71
|
+
"""
|
72
|
+
if self.local_vars_configuration.client_side_validation and slack_urls is None: # noqa: E501
|
73
|
+
raise ValueError("Invalid value for `slack_urls`, must not be `None`") # noqa: E501
|
74
|
+
|
75
|
+
self._slack_urls = slack_urls
|
76
|
+
|
77
|
+
def to_dict(self):
|
78
|
+
"""Returns the model properties as a dict"""
|
79
|
+
result = {}
|
80
|
+
|
81
|
+
for attr, _ in six.iteritems(self.openapi_types):
|
82
|
+
value = getattr(self, attr)
|
83
|
+
if isinstance(value, list):
|
84
|
+
result[attr] = list(map(
|
85
|
+
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
86
|
+
value
|
87
|
+
))
|
88
|
+
elif hasattr(value, "to_dict"):
|
89
|
+
result[attr] = value.to_dict()
|
90
|
+
elif isinstance(value, dict):
|
91
|
+
result[attr] = dict(map(
|
92
|
+
lambda item: (item[0], item[1].to_dict())
|
93
|
+
if hasattr(item[1], "to_dict") else item,
|
94
|
+
value.items()
|
95
|
+
))
|
96
|
+
else:
|
97
|
+
result[attr] = value
|
98
|
+
|
99
|
+
return result
|
100
|
+
|
101
|
+
def to_str(self):
|
102
|
+
"""Returns the string representation of the model"""
|
103
|
+
return pprint.pformat(self.to_dict())
|
104
|
+
|
105
|
+
def __repr__(self):
|
106
|
+
"""For `print` and `pprint`"""
|
107
|
+
return self.to_str()
|
108
|
+
|
109
|
+
def __eq__(self, other):
|
110
|
+
"""Returns true if both objects are equal"""
|
111
|
+
if not isinstance(other, NotificationChannelSlackConfig):
|
112
|
+
return False
|
113
|
+
|
114
|
+
return self.to_dict() == other.to_dict()
|
115
|
+
|
116
|
+
def __ne__(self, other):
|
117
|
+
"""Returns true if both objects are not equal"""
|
118
|
+
if not isinstance(other, NotificationChannelSlackConfig):
|
119
|
+
return True
|
120
|
+
|
121
|
+
return self.to_dict() != other.to_dict()
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
"""
|
4
|
+
Managed Ray API
|
5
|
+
|
6
|
+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501
|
7
|
+
|
8
|
+
The version of the OpenAPI document: 0.1.0
|
9
|
+
Generated by: https://openapi-generator.tech
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
import pprint
|
14
|
+
import re # noqa: F401
|
15
|
+
|
16
|
+
import six
|
17
|
+
|
18
|
+
from openapi_client.configuration import Configuration
|
19
|
+
|
20
|
+
|
21
|
+
class UpdateCloudCollaborator(object):
|
22
|
+
"""NOTE: This class is auto generated by OpenAPI Generator.
|
23
|
+
Ref: https://openapi-generator.tech
|
24
|
+
|
25
|
+
Do not edit the class manually.
|
26
|
+
"""
|
27
|
+
|
28
|
+
"""
|
29
|
+
Attributes:
|
30
|
+
openapi_types (dict): The key is attribute name
|
31
|
+
and the value is attribute type.
|
32
|
+
attribute_map (dict): The key is attribute name
|
33
|
+
and the value is json key in definition.
|
34
|
+
"""
|
35
|
+
openapi_types = {
|
36
|
+
'permission_level': 'PermissionLevel'
|
37
|
+
}
|
38
|
+
|
39
|
+
attribute_map = {
|
40
|
+
'permission_level': 'permission_level'
|
41
|
+
}
|
42
|
+
|
43
|
+
def __init__(self, permission_level=None, local_vars_configuration=None): # noqa: E501
|
44
|
+
"""UpdateCloudCollaborator - a model defined in OpenAPI""" # noqa: E501
|
45
|
+
if local_vars_configuration is None:
|
46
|
+
local_vars_configuration = Configuration()
|
47
|
+
self.local_vars_configuration = local_vars_configuration
|
48
|
+
|
49
|
+
self._permission_level = None
|
50
|
+
self.discriminator = None
|
51
|
+
|
52
|
+
self.permission_level = permission_level
|
53
|
+
|
54
|
+
@property
|
55
|
+
def permission_level(self):
|
56
|
+
"""Gets the permission_level of this UpdateCloudCollaborator. # noqa: E501
|
57
|
+
|
58
|
+
|
59
|
+
:return: The permission_level of this UpdateCloudCollaborator. # noqa: E501
|
60
|
+
:rtype: PermissionLevel
|
61
|
+
"""
|
62
|
+
return self._permission_level
|
63
|
+
|
64
|
+
@permission_level.setter
|
65
|
+
def permission_level(self, permission_level):
|
66
|
+
"""Sets the permission_level of this UpdateCloudCollaborator.
|
67
|
+
|
68
|
+
|
69
|
+
:param permission_level: The permission_level of this UpdateCloudCollaborator. # noqa: E501
|
70
|
+
:type: PermissionLevel
|
71
|
+
"""
|
72
|
+
if self.local_vars_configuration.client_side_validation and permission_level is None: # noqa: E501
|
73
|
+
raise ValueError("Invalid value for `permission_level`, must not be `None`") # noqa: E501
|
74
|
+
|
75
|
+
self._permission_level = permission_level
|
76
|
+
|
77
|
+
def to_dict(self):
|
78
|
+
"""Returns the model properties as a dict"""
|
79
|
+
result = {}
|
80
|
+
|
81
|
+
for attr, _ in six.iteritems(self.openapi_types):
|
82
|
+
value = getattr(self, attr)
|
83
|
+
if isinstance(value, list):
|
84
|
+
result[attr] = list(map(
|
85
|
+
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
86
|
+
value
|
87
|
+
))
|
88
|
+
elif hasattr(value, "to_dict"):
|
89
|
+
result[attr] = value.to_dict()
|
90
|
+
elif isinstance(value, dict):
|
91
|
+
result[attr] = dict(map(
|
92
|
+
lambda item: (item[0], item[1].to_dict())
|
93
|
+
if hasattr(item[1], "to_dict") else item,
|
94
|
+
value.items()
|
95
|
+
))
|
96
|
+
else:
|
97
|
+
result[attr] = value
|
98
|
+
|
99
|
+
return result
|
100
|
+
|
101
|
+
def to_str(self):
|
102
|
+
"""Returns the string representation of the model"""
|
103
|
+
return pprint.pformat(self.to_dict())
|
104
|
+
|
105
|
+
def __repr__(self):
|
106
|
+
"""For `print` and `pprint`"""
|
107
|
+
return self.to_str()
|
108
|
+
|
109
|
+
def __eq__(self, other):
|
110
|
+
"""Returns true if both objects are equal"""
|
111
|
+
if not isinstance(other, UpdateCloudCollaborator):
|
112
|
+
return False
|
113
|
+
|
114
|
+
return self.to_dict() == other.to_dict()
|
115
|
+
|
116
|
+
def __ne__(self, other):
|
117
|
+
"""Returns true if both objects are not equal"""
|
118
|
+
if not isinstance(other, UpdateCloudCollaborator):
|
119
|
+
return True
|
120
|
+
|
121
|
+
return self.to_dict() != other.to_dict()
|
@@ -1,7 +1,9 @@
|
|
1
|
+
from io import StringIO
|
1
2
|
import re
|
2
3
|
from typing import List, Optional
|
3
4
|
|
4
5
|
import click
|
6
|
+
import yaml
|
5
7
|
|
6
8
|
import anyscale
|
7
9
|
from anyscale.cli_logger import BlockLogger
|
@@ -321,11 +323,12 @@ def cloud_config_get(
|
|
321
323
|
"The positional argument CLOUD_NAME and the keyword argument --name "
|
322
324
|
"were both provided. Please only provide one of these two arguments."
|
323
325
|
)
|
324
|
-
|
325
|
-
|
326
|
-
cloud_name=cloud_name or name, cloud_id=cloud_id,
|
327
|
-
)
|
326
|
+
config = CloudController().get_cloud_config(
|
327
|
+
cloud_name=cloud_name or name, cloud_id=cloud_id,
|
328
328
|
)
|
329
|
+
stream = StringIO()
|
330
|
+
yaml.dump(config.spec, stream)
|
331
|
+
print(stream.getvalue())
|
329
332
|
|
330
333
|
|
331
334
|
@cloud_config_group.command(
|
@@ -354,13 +357,26 @@ def cloud_config_get(
|
|
354
357
|
"extra data transfer cost from the cloud provider by enabling this feature."
|
355
358
|
),
|
356
359
|
)
|
360
|
+
@click.option(
|
361
|
+
"--spec-file",
|
362
|
+
type=str,
|
363
|
+
required=False,
|
364
|
+
help="Provide a path to a specification file.",
|
365
|
+
)
|
357
366
|
def cloud_config_update(
|
358
367
|
cloud_name: Optional[str],
|
359
368
|
name: Optional[str],
|
360
369
|
cloud_id: Optional[str],
|
361
370
|
enable_log_ingestion: Optional[bool],
|
371
|
+
spec_file: Optional[str],
|
362
372
|
) -> None:
|
373
|
+
if any([enable_log_ingestion is not None]) and spec_file:
|
374
|
+
raise click.ClickException(
|
375
|
+
"Please provide only one of the following arguments: --enable-log-ingestion, --disable-log-ingestion, --spec-file."
|
376
|
+
)
|
377
|
+
|
363
378
|
if any([enable_log_ingestion is not None]):
|
379
|
+
# TODO: enable_log_ingestion should be unified into cloud deployment config.
|
364
380
|
if enable_log_ingestion is True:
|
365
381
|
consent_message = click.prompt(
|
366
382
|
"--enable-log-ingestion is specified. Please note the logs produced by "
|
@@ -390,6 +406,10 @@ def cloud_config_update(
|
|
390
406
|
cloud_id=cloud_id,
|
391
407
|
enable_log_ingestion=enable_log_ingestion,
|
392
408
|
)
|
409
|
+
elif spec_file:
|
410
|
+
CloudController().update_cloud_config(
|
411
|
+
cloud_name=cloud_name or name, cloud_id=cloud_id, spec_file=spec_file,
|
412
|
+
)
|
393
413
|
else:
|
394
414
|
raise click.ClickException(
|
395
415
|
"Please provide at least one of the following arguments: --enable-log-ingestion, --disable-log-ingestion."
|
@@ -9,7 +9,9 @@ import yaml
|
|
9
9
|
|
10
10
|
from anyscale._private.models.image_uri import ImageURI
|
11
11
|
from anyscale.cli_logger import BlockLogger
|
12
|
+
from anyscale.commands import command_examples
|
12
13
|
from anyscale.commands.util import (
|
14
|
+
AnyscaleCommand,
|
13
15
|
convert_kv_strings_to_dict,
|
14
16
|
LegacyAnyscaleCommand,
|
15
17
|
override_env_vars,
|
@@ -736,3 +738,61 @@ def terminate(
|
|
736
738
|
project_id=project_id,
|
737
739
|
)
|
738
740
|
service_controller.terminate(service_id)
|
741
|
+
|
742
|
+
|
743
|
+
@service_cli.command(
|
744
|
+
name="archive",
|
745
|
+
help="Archive a service.",
|
746
|
+
cls=AnyscaleCommand,
|
747
|
+
example=command_examples.SERVICE_ARCHIVE_EXAMPLE,
|
748
|
+
)
|
749
|
+
@click.option(
|
750
|
+
"-n", "--name", required=False, default=None, type=str, help="Name of the service.",
|
751
|
+
)
|
752
|
+
@click.option(
|
753
|
+
"-f",
|
754
|
+
"--config-file",
|
755
|
+
required=False,
|
756
|
+
default=None,
|
757
|
+
type=str,
|
758
|
+
help="Path to a YAML config file to read the name from.",
|
759
|
+
)
|
760
|
+
@click.option(
|
761
|
+
"--cloud",
|
762
|
+
required=False,
|
763
|
+
default=None,
|
764
|
+
type=str,
|
765
|
+
help="The Anyscale Cloud to run this workload on. If not provided, the organization default will be used (or, if running in a workspace, the cloud of the workspace).",
|
766
|
+
)
|
767
|
+
@click.option(
|
768
|
+
"--project",
|
769
|
+
required=False,
|
770
|
+
default=None,
|
771
|
+
type=str,
|
772
|
+
help="Named project to use for the service. If not provided, the default project for the cloud will be used (or, if running in a workspace, the project of the workspace).",
|
773
|
+
)
|
774
|
+
def archive(
|
775
|
+
name: Optional[str],
|
776
|
+
config_file: Optional[str],
|
777
|
+
cloud: Optional[str],
|
778
|
+
project: Optional[str],
|
779
|
+
) -> None:
|
780
|
+
"""Archive a service.
|
781
|
+
|
782
|
+
To specify the service by name, use the --name flag. To specify the service by id, use the --id flag. Either name or
|
783
|
+
id should be used, specifying both will result in an error.
|
784
|
+
"""
|
785
|
+
if name is not None and config_file is not None:
|
786
|
+
raise click.ClickException(
|
787
|
+
"Only one of '--name' and '--config-file' can be provided."
|
788
|
+
)
|
789
|
+
|
790
|
+
if config_file is not None:
|
791
|
+
name = _read_name_from_config_file(config_file)
|
792
|
+
|
793
|
+
if name is None:
|
794
|
+
raise click.ClickException(
|
795
|
+
"Service name must be provided using '--name' or in a config file using '-f'."
|
796
|
+
)
|
797
|
+
|
798
|
+
anyscale.service.archive(name=name, cloud=cloud, project=project)
|
@@ -5,6 +5,7 @@ Fetches data required and formats output for `anyscale cloud` commands.
|
|
5
5
|
import copy
|
6
6
|
import json
|
7
7
|
from os import getenv
|
8
|
+
import pathlib
|
8
9
|
import re
|
9
10
|
import secrets
|
10
11
|
import time
|
@@ -15,6 +16,7 @@ import boto3
|
|
15
16
|
from botocore.exceptions import ClientError, NoCredentialsError
|
16
17
|
import click
|
17
18
|
from click import Abort, ClickException
|
19
|
+
import yaml
|
18
20
|
|
19
21
|
from anyscale import __version__ as anyscale_version
|
20
22
|
from anyscale.aws_iam_policies import get_anyscale_iam_permissions_ec2_restricted
|
@@ -24,6 +26,7 @@ from anyscale.client.openapi_client.models import (
|
|
24
26
|
CloudAnalyticsEventCloudResource,
|
25
27
|
CloudAnalyticsEventCommandName,
|
26
28
|
CloudAnalyticsEventName,
|
29
|
+
CloudDeploymentConfig,
|
27
30
|
CloudProviders,
|
28
31
|
CloudState,
|
29
32
|
CloudWithCloudResource,
|
@@ -59,7 +62,6 @@ from anyscale.cloud_resource import (
|
|
59
62
|
)
|
60
63
|
from anyscale.cloud_utils import (
|
61
64
|
get_cloud_id_and_name,
|
62
|
-
get_cloud_json_from_id,
|
63
65
|
get_cloud_resource_by_cloud_id,
|
64
66
|
get_organization_id,
|
65
67
|
)
|
@@ -1382,23 +1384,28 @@ class CloudController(BaseController):
|
|
1382
1384
|
|
1383
1385
|
def get_cloud_config(
|
1384
1386
|
self, cloud_name: Optional[str] = None, cloud_id: Optional[str] = None,
|
1385
|
-
) ->
|
1387
|
+
) -> CloudDeploymentConfig:
|
1386
1388
|
"""Get a cloud's current JSON configuration."""
|
1387
1389
|
|
1388
1390
|
cloud_id, cloud_name = get_cloud_id_and_name(
|
1389
1391
|
self.api_client, cloud_id, cloud_name
|
1390
1392
|
)
|
1391
1393
|
|
1392
|
-
|
1394
|
+
# In the future we will expose cloud_deployment id as a parameter, for now, it's just a placeholder.
|
1395
|
+
config: CloudDeploymentConfig = self.api_client.get_cloud_deployment_config_api_v2_clouds_cloud_id_deployment_cloud_deployment_id_config_get(
|
1396
|
+
cloud_id=cloud_id, cloud_deployment_id="default"
|
1397
|
+
).result
|
1398
|
+
|
1399
|
+
return config
|
1393
1400
|
|
1394
1401
|
def update_cloud_config(
|
1395
1402
|
self,
|
1396
1403
|
cloud_name: Optional[str] = None,
|
1397
1404
|
cloud_id: Optional[str] = None,
|
1398
1405
|
enable_log_ingestion: Optional[bool] = None,
|
1406
|
+
spec_file: Optional[str] = None,
|
1399
1407
|
):
|
1400
1408
|
"""Update a cloud's configuration."""
|
1401
|
-
|
1402
1409
|
cloud_id, cloud_name = get_cloud_id_and_name(
|
1403
1410
|
self.api_client, cloud_id, cloud_name
|
1404
1411
|
)
|
@@ -1410,6 +1417,24 @@ class CloudController(BaseController):
|
|
1410
1417
|
f"Successfully updated log ingestion configuration for cloud, "
|
1411
1418
|
f"{cloud_id} to {enable_log_ingestion}"
|
1412
1419
|
)
|
1420
|
+
elif spec_file is not None:
|
1421
|
+
path = pathlib.Path(spec_file)
|
1422
|
+
if not path.exists():
|
1423
|
+
raise FileNotFoundError(f"File {spec_file} does not exist.")
|
1424
|
+
|
1425
|
+
if not path.is_file():
|
1426
|
+
raise ValueError(f"File {spec_file} is not a file.")
|
1427
|
+
|
1428
|
+
spec = yaml.safe_load(path.read_text())
|
1429
|
+
config = CloudDeploymentConfig(spec=spec)
|
1430
|
+
self.api_client.update_cloud_deployment_config_api_v2_clouds_cloud_id_deployment_cloud_deployment_id_config_put(
|
1431
|
+
cloud_id=cloud_id,
|
1432
|
+
cloud_deployment_id="default",
|
1433
|
+
cloud_deployment_config=config,
|
1434
|
+
)
|
1435
|
+
self.log.info(
|
1436
|
+
f"Successfully updated cloud configuration for cloud {cloud_name}"
|
1437
|
+
)
|
1413
1438
|
|
1414
1439
|
def set_default_cloud(
|
1415
1440
|
self, cloud_name: Optional[str], cloud_id: Optional[str],
|