datacrunch 1.15.0__py3-none-any.whl → 1.16.0__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.
- datacrunch/InferenceClient/inference_client.py +200 -65
- datacrunch/__init__.py +2 -0
- datacrunch/_version.py +6 -0
- datacrunch/authentication/authentication.py +7 -14
- datacrunch/balance/balance.py +1 -3
- datacrunch/constants.py +19 -17
- datacrunch/containers/containers.py +151 -123
- datacrunch/datacrunch.py +18 -18
- datacrunch/helpers.py +7 -2
- datacrunch/http_client/http_client.py +14 -14
- datacrunch/images/images.py +9 -3
- datacrunch/instance_types/instance_types.py +42 -35
- datacrunch/instances/instances.py +62 -50
- datacrunch/locations/locations.py +1 -2
- datacrunch/ssh_keys/ssh_keys.py +3 -4
- datacrunch/startup_scripts/startup_scripts.py +10 -8
- datacrunch/volume_types/volume_types.py +10 -8
- datacrunch/volumes/volumes.py +60 -73
- {datacrunch-1.15.0.dist-info → datacrunch-1.16.0.dist-info}/METADATA +46 -72
- datacrunch-1.16.0.dist-info/RECORD +35 -0
- datacrunch-1.16.0.dist-info/WHEEL +4 -0
- datacrunch/__version__.py +0 -1
- datacrunch-1.15.0.dist-info/RECORD +0 -69
- datacrunch-1.15.0.dist-info/WHEEL +0 -5
- datacrunch-1.15.0.dist-info/licenses/LICENSE +0 -21
- datacrunch-1.15.0.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/integration_tests/__init__.py +0 -0
- tests/integration_tests/conftest.py +0 -20
- tests/integration_tests/test_instances.py +0 -36
- tests/integration_tests/test_locations.py +0 -65
- tests/integration_tests/test_volumes.py +0 -94
- tests/unit_tests/__init__.py +0 -0
- tests/unit_tests/authentication/__init__.py +0 -0
- tests/unit_tests/authentication/test_authentication.py +0 -202
- tests/unit_tests/balance/__init__.py +0 -0
- tests/unit_tests/balance/test_balance.py +0 -25
- tests/unit_tests/conftest.py +0 -21
- tests/unit_tests/containers/__init__.py +0 -1
- tests/unit_tests/containers/test_containers.py +0 -959
- tests/unit_tests/http_client/__init__.py +0 -0
- tests/unit_tests/http_client/test_http_client.py +0 -193
- tests/unit_tests/images/__init__.py +0 -0
- tests/unit_tests/images/test_images.py +0 -41
- tests/unit_tests/instance_types/__init__.py +0 -0
- tests/unit_tests/instance_types/test_instance_types.py +0 -87
- tests/unit_tests/instances/__init__.py +0 -0
- tests/unit_tests/instances/test_instances.py +0 -483
- tests/unit_tests/ssh_keys/__init__.py +0 -0
- tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -198
- tests/unit_tests/startup_scripts/__init__.py +0 -0
- tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -196
- tests/unit_tests/test_datacrunch.py +0 -65
- tests/unit_tests/test_exceptions.py +0 -33
- tests/unit_tests/volume_types/__init__.py +0 -0
- tests/unit_tests/volume_types/test_volume_types.py +0 -50
- tests/unit_tests/volumes/__init__.py +0 -0
- tests/unit_tests/volumes/test_volumes.py +0 -641
|
File without changes
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
import responses # https://github.com/getsentry/responses
|
|
3
|
-
from unittest.mock import Mock
|
|
4
|
-
from datacrunch.exceptions import APIException
|
|
5
|
-
|
|
6
|
-
INVALID_REQUEST = 'invalid_request'
|
|
7
|
-
INVALID_REQUEST_MESSAGE = 'Your existence is invalid'
|
|
8
|
-
|
|
9
|
-
UNAUTHORIZED_REQUEST = 'unauthorized_request'
|
|
10
|
-
UNAUTHORIZED_REQUEST_MESSAGE = 'Access token is missing or invalid'
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TestHttpClient:
|
|
14
|
-
def test_add_base_url(self, http_client):
|
|
15
|
-
# arrange
|
|
16
|
-
path = "/test"
|
|
17
|
-
base = http_client._base_url
|
|
18
|
-
|
|
19
|
-
# act
|
|
20
|
-
url = http_client._add_base_url(path)
|
|
21
|
-
|
|
22
|
-
# assert
|
|
23
|
-
assert base == http_client._base_url
|
|
24
|
-
assert url == base + path
|
|
25
|
-
|
|
26
|
-
def test_generate_bearer_header(self, http_client):
|
|
27
|
-
bearer_string = http_client._generate_bearer_header()
|
|
28
|
-
access_token = http_client._auth_service._access_token
|
|
29
|
-
|
|
30
|
-
assert type(bearer_string) == str
|
|
31
|
-
assert bearer_string == f'Bearer {access_token}'
|
|
32
|
-
|
|
33
|
-
def test_generate_user_agent(self, http_client):
|
|
34
|
-
# arrange
|
|
35
|
-
version = http_client._version
|
|
36
|
-
client_id_truncated = http_client._auth_service._client_id[0:10]
|
|
37
|
-
|
|
38
|
-
# act
|
|
39
|
-
user_agent_string = http_client._generate_user_agent()
|
|
40
|
-
|
|
41
|
-
# assert
|
|
42
|
-
assert type(user_agent_string) == str
|
|
43
|
-
assert user_agent_string == f'datacrunch-python-v{version}-{client_id_truncated}'
|
|
44
|
-
|
|
45
|
-
def test_generate_headers(self, http_client):
|
|
46
|
-
# arrange / act
|
|
47
|
-
headers = http_client._generate_headers()
|
|
48
|
-
authorization_string = http_client._generate_bearer_header()
|
|
49
|
-
user_agent_string = http_client._generate_user_agent()
|
|
50
|
-
|
|
51
|
-
# assert
|
|
52
|
-
assert type(headers) == dict
|
|
53
|
-
assert type(headers['Content-Type']) == str
|
|
54
|
-
assert type(headers['Authorization']) == str
|
|
55
|
-
assert type(headers['User-Agent']) == str
|
|
56
|
-
assert headers['Content-Type'] == 'application/json'
|
|
57
|
-
assert headers['Authorization'] == authorization_string
|
|
58
|
-
assert headers['User-Agent'] == user_agent_string
|
|
59
|
-
|
|
60
|
-
def test_refresh_token_if_expired_refresh_successful(self, http_client):
|
|
61
|
-
# act
|
|
62
|
-
http_client._refresh_token_if_expired()
|
|
63
|
-
|
|
64
|
-
# assert
|
|
65
|
-
http_client._auth_service.refresh.assert_called_once()
|
|
66
|
-
http_client._auth_service.is_expired.assert_called()
|
|
67
|
-
http_client._auth_service.authenticate.assert_called_once()
|
|
68
|
-
|
|
69
|
-
def test_refresh_token_if_expired_refresh_failed(self, http_client):
|
|
70
|
-
# arrange - make refresh raise an exception
|
|
71
|
-
http_client._auth_service.refresh = Mock(side_effect=Exception())
|
|
72
|
-
|
|
73
|
-
# act
|
|
74
|
-
http_client._refresh_token_if_expired()
|
|
75
|
-
|
|
76
|
-
# assert
|
|
77
|
-
http_client._auth_service.refresh.assert_called_once()
|
|
78
|
-
http_client._auth_service.is_expired.assert_called()
|
|
79
|
-
assert http_client._auth_service.authenticate.call_count == 2
|
|
80
|
-
|
|
81
|
-
def test_get_successful(self, http_client):
|
|
82
|
-
# arrange - add response mock
|
|
83
|
-
responses.add(
|
|
84
|
-
method=responses.GET,
|
|
85
|
-
url=(http_client._base_url + '/test'),
|
|
86
|
-
status=200, body='{}',
|
|
87
|
-
content_type='application/json'
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
# act
|
|
91
|
-
response = http_client.get('/test')
|
|
92
|
-
|
|
93
|
-
# assert
|
|
94
|
-
assert response.ok is True
|
|
95
|
-
assert response.status_code == 200
|
|
96
|
-
assert response.text == '{}'
|
|
97
|
-
assert response.headers['Content-Type'] == 'application/json'
|
|
98
|
-
assert response.json() == {}
|
|
99
|
-
assert responses.assert_call_count(http_client._base_url + '/test', 1) is True
|
|
100
|
-
|
|
101
|
-
def test_post_successful(self, http_client):
|
|
102
|
-
# arrange - add response mock
|
|
103
|
-
responses.add(
|
|
104
|
-
method=responses.POST,
|
|
105
|
-
url=(http_client._base_url + '/test'),
|
|
106
|
-
status=200, body='{}',
|
|
107
|
-
content_type='application/json'
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
# act
|
|
111
|
-
response = http_client.post('/test', params={})
|
|
112
|
-
|
|
113
|
-
# assert
|
|
114
|
-
assert response.ok is True
|
|
115
|
-
assert response.status_code == 200
|
|
116
|
-
assert response.text == '{}'
|
|
117
|
-
assert response.headers['Content-Type'] == 'application/json'
|
|
118
|
-
assert response.json() == {}
|
|
119
|
-
assert responses.assert_call_count(http_client._base_url + '/test', 1) is True
|
|
120
|
-
|
|
121
|
-
def test_delete_successful(self, http_client):
|
|
122
|
-
# arrange - add response mock
|
|
123
|
-
responses.add(
|
|
124
|
-
method=responses.DELETE,
|
|
125
|
-
url=(http_client._base_url + '/test'),
|
|
126
|
-
status=200,
|
|
127
|
-
content_type='application/json'
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
# act
|
|
131
|
-
response = http_client.delete('/test')
|
|
132
|
-
|
|
133
|
-
# assert
|
|
134
|
-
assert response.ok is True
|
|
135
|
-
assert response.status_code == 200
|
|
136
|
-
assert response.headers['Content-Type'] == 'application/json'
|
|
137
|
-
assert responses.assert_call_count(http_client._base_url + '/test', 1) is True
|
|
138
|
-
|
|
139
|
-
def test_get_failed(self, http_client):
|
|
140
|
-
# arrange - add response mock
|
|
141
|
-
responses.add(
|
|
142
|
-
method=responses.GET,
|
|
143
|
-
url=(http_client._base_url + '/test'),
|
|
144
|
-
status=401,
|
|
145
|
-
json={'code': UNAUTHORIZED_REQUEST, 'message': UNAUTHORIZED_REQUEST_MESSAGE},
|
|
146
|
-
content_type='application/json'
|
|
147
|
-
)
|
|
148
|
-
error_str = f'error code: {UNAUTHORIZED_REQUEST}\nmessage: {UNAUTHORIZED_REQUEST_MESSAGE}'
|
|
149
|
-
|
|
150
|
-
# act
|
|
151
|
-
with pytest.raises(APIException) as excinfo:
|
|
152
|
-
http_client.get('/test')
|
|
153
|
-
|
|
154
|
-
# assert
|
|
155
|
-
assert excinfo.value.code == UNAUTHORIZED_REQUEST
|
|
156
|
-
assert excinfo.value.message == UNAUTHORIZED_REQUEST_MESSAGE
|
|
157
|
-
assert excinfo.value.__str__() == error_str
|
|
158
|
-
|
|
159
|
-
def test_post_failed(self, http_client):
|
|
160
|
-
# arrange - add response mock
|
|
161
|
-
responses.add(
|
|
162
|
-
method=responses.POST,
|
|
163
|
-
url=(http_client._base_url + '/test'),
|
|
164
|
-
status=400,
|
|
165
|
-
json={'code': INVALID_REQUEST, 'message': INVALID_REQUEST_MESSAGE},
|
|
166
|
-
content_type='application/json'
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
# act
|
|
170
|
-
with pytest.raises(APIException) as excinfo:
|
|
171
|
-
http_client.post('/test', json={'data': '42'})
|
|
172
|
-
|
|
173
|
-
# assert
|
|
174
|
-
assert excinfo.value.code == INVALID_REQUEST
|
|
175
|
-
assert excinfo.value.message == INVALID_REQUEST_MESSAGE
|
|
176
|
-
|
|
177
|
-
def test_delete_failed(self, http_client):
|
|
178
|
-
# arrange - add response mock
|
|
179
|
-
responses.add(
|
|
180
|
-
method=responses.DELETE,
|
|
181
|
-
url=(http_client._base_url + '/test'),
|
|
182
|
-
status=400,
|
|
183
|
-
json={'code': INVALID_REQUEST, 'message': INVALID_REQUEST_MESSAGE},
|
|
184
|
-
content_type='application/json'
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
# act
|
|
188
|
-
with pytest.raises(APIException) as excinfo:
|
|
189
|
-
http_client.delete('/test', json={'data': '42'})
|
|
190
|
-
|
|
191
|
-
# assert
|
|
192
|
-
assert excinfo.value.code == INVALID_REQUEST
|
|
193
|
-
assert excinfo.value.message == INVALID_REQUEST_MESSAGE
|
|
File without changes
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import responses # https://github.com/getsentry/responses
|
|
2
|
-
|
|
3
|
-
from datacrunch.images.images import ImagesService, Image
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_images(http_client):
|
|
7
|
-
# arrange - add response mock
|
|
8
|
-
responses.add(
|
|
9
|
-
responses.GET,
|
|
10
|
-
http_client._base_url + "/images",
|
|
11
|
-
json=[
|
|
12
|
-
{
|
|
13
|
-
"id": "0888da25-bb0d-41cc-a191-dccae45d96fd",
|
|
14
|
-
"name": "Ubuntu 20.04 + CUDA 11.0",
|
|
15
|
-
"details": [
|
|
16
|
-
"Ubuntu 20.04",
|
|
17
|
-
"CUDA 11.0"
|
|
18
|
-
],
|
|
19
|
-
"image_type": "ubuntu-20.04-cuda-11.0"
|
|
20
|
-
}
|
|
21
|
-
],
|
|
22
|
-
status=200
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
image_service = ImagesService(http_client)
|
|
26
|
-
|
|
27
|
-
# act
|
|
28
|
-
images = image_service.get()
|
|
29
|
-
|
|
30
|
-
# assert
|
|
31
|
-
assert type(images) == list
|
|
32
|
-
assert len(images) == 1
|
|
33
|
-
assert type(images[0]) == Image
|
|
34
|
-
assert images[0].id == '0888da25-bb0d-41cc-a191-dccae45d96fd'
|
|
35
|
-
assert images[0].name == 'Ubuntu 20.04 + CUDA 11.0'
|
|
36
|
-
assert images[0].image_type == 'ubuntu-20.04-cuda-11.0'
|
|
37
|
-
assert type(images[0].details) == list
|
|
38
|
-
assert images[0].details[0] == "Ubuntu 20.04"
|
|
39
|
-
assert images[0].details[1] == "CUDA 11.0"
|
|
40
|
-
assert type(images[0].__str__()) == str
|
|
41
|
-
|
|
File without changes
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import responses # https://github.com/getsentry/responses
|
|
2
|
-
|
|
3
|
-
from datacrunch.instance_types.instance_types import InstanceTypesService, InstanceType
|
|
4
|
-
|
|
5
|
-
TYPE_ID = "01cf5dc1-a5d2-4972-ae4e-d429115d055b"
|
|
6
|
-
CPU_DESCRIPTION = "48 CPU 3.5GHz"
|
|
7
|
-
NUMBER_OF_CORES = 48
|
|
8
|
-
GPU_DESCRIPTION = "8x NVidia Tesla V100"
|
|
9
|
-
NUMBER_OF_GPUS = 8
|
|
10
|
-
MEMORY_DESCRIPTION = "192GB RAM"
|
|
11
|
-
MEMORY_SIZE = 192
|
|
12
|
-
GPU_MEMORY_DESCRIPTION = "128GB VRAM"
|
|
13
|
-
GPU_MEMORY_SIZE = 128
|
|
14
|
-
STORAGE_DESCRIPTION = "1800GB NVME"
|
|
15
|
-
STORAGE_SIZE = 1800
|
|
16
|
-
INSTANCE_TYPE_DESCRIPTION = "Dedicated Bare metal Server"
|
|
17
|
-
PRICE_PER_HOUR = 5.0
|
|
18
|
-
SPOT_PRICE_PER_HOUR = 2.5
|
|
19
|
-
INSTANCE_TYPE = "8V100.48M"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_instance_types(http_client):
|
|
23
|
-
# arrange - add response mock
|
|
24
|
-
responses.add(
|
|
25
|
-
responses.GET,
|
|
26
|
-
http_client._base_url + "/instance-types",
|
|
27
|
-
json=[
|
|
28
|
-
{
|
|
29
|
-
"id": TYPE_ID,
|
|
30
|
-
"cpu": {
|
|
31
|
-
"description": CPU_DESCRIPTION,
|
|
32
|
-
"number_of_cores": NUMBER_OF_CORES
|
|
33
|
-
},
|
|
34
|
-
"gpu": {
|
|
35
|
-
"description": GPU_DESCRIPTION,
|
|
36
|
-
"number_of_gpus": NUMBER_OF_GPUS
|
|
37
|
-
},
|
|
38
|
-
"memory": {
|
|
39
|
-
"description": MEMORY_DESCRIPTION,
|
|
40
|
-
"size_in_gigabytes": MEMORY_SIZE
|
|
41
|
-
},
|
|
42
|
-
"gpu_memory": {
|
|
43
|
-
"description": GPU_MEMORY_DESCRIPTION,
|
|
44
|
-
"size_in_gigabytes": GPU_MEMORY_SIZE
|
|
45
|
-
},
|
|
46
|
-
"storage": {
|
|
47
|
-
"description": STORAGE_DESCRIPTION,
|
|
48
|
-
"size_in_gigabytes": STORAGE_SIZE
|
|
49
|
-
},
|
|
50
|
-
"description": INSTANCE_TYPE_DESCRIPTION,
|
|
51
|
-
"price_per_hour": "5.00",
|
|
52
|
-
"spot_price": "2.50",
|
|
53
|
-
"instance_type": INSTANCE_TYPE
|
|
54
|
-
}
|
|
55
|
-
],
|
|
56
|
-
status=200
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
instance_types_service = InstanceTypesService(http_client)
|
|
60
|
-
|
|
61
|
-
# act
|
|
62
|
-
instance_types = instance_types_service.get()
|
|
63
|
-
instance_type = instance_types[0]
|
|
64
|
-
|
|
65
|
-
# assert
|
|
66
|
-
assert type(instance_types) == list
|
|
67
|
-
assert len(instance_types) == 1
|
|
68
|
-
assert type(instance_type) == InstanceType
|
|
69
|
-
assert instance_type.id == TYPE_ID
|
|
70
|
-
assert instance_type.description == INSTANCE_TYPE_DESCRIPTION
|
|
71
|
-
assert instance_type.price_per_hour == PRICE_PER_HOUR
|
|
72
|
-
assert instance_type.spot_price_per_hour == SPOT_PRICE_PER_HOUR
|
|
73
|
-
assert instance_type.instance_type == INSTANCE_TYPE
|
|
74
|
-
assert type(instance_type.cpu) == dict
|
|
75
|
-
assert type(instance_type.gpu) == dict
|
|
76
|
-
assert type(instance_type.memory) == dict
|
|
77
|
-
assert type(instance_type.storage) == dict
|
|
78
|
-
assert instance_type.cpu['description'] == CPU_DESCRIPTION
|
|
79
|
-
assert instance_type.gpu['description'] == GPU_DESCRIPTION
|
|
80
|
-
assert instance_type.memory['description'] == MEMORY_DESCRIPTION
|
|
81
|
-
assert instance_type.gpu_memory['description'] == GPU_MEMORY_DESCRIPTION
|
|
82
|
-
assert instance_type.storage['description'] == STORAGE_DESCRIPTION
|
|
83
|
-
assert instance_type.cpu['number_of_cores'] == NUMBER_OF_CORES
|
|
84
|
-
assert instance_type.gpu['number_of_gpus'] == NUMBER_OF_GPUS
|
|
85
|
-
assert instance_type.memory['size_in_gigabytes'] == MEMORY_SIZE
|
|
86
|
-
assert instance_type.gpu_memory['size_in_gigabytes'] == GPU_MEMORY_SIZE
|
|
87
|
-
assert instance_type.storage['size_in_gigabytes'] == STORAGE_SIZE
|
|
File without changes
|