trainml 0.5.9__py3-none-any.whl → 0.5.12__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.
- tests/integration/projects/conftest.py +3 -1
- tests/integration/projects/test_projects_credentials_integration.py +45 -0
- tests/integration/projects/test_projects_data_connectors_integration.py +44 -0
- tests/integration/projects/test_projects_datastores_integration.py +42 -0
- tests/integration/projects/test_projects_secrets_integration.py +1 -1
- tests/integration/projects/test_projects_services_integration.py +44 -0
- tests/integration/test_checkpoints_integration.py +1 -2
- tests/integration/test_models_integration.py +0 -1
- tests/unit/cli/projects/__init__.py +0 -0
- tests/unit/cli/projects/test_cli_project_credential_unit.py +26 -0
- tests/unit/cli/projects/test_cli_project_data_connector_unit.py +28 -0
- tests/unit/cli/projects/test_cli_project_datastore_unit.py +26 -0
- tests/unit/cli/projects/test_cli_project_key_unit.py +26 -0
- tests/unit/cli/projects/test_cli_project_secret_unit.py +26 -0
- tests/unit/cli/projects/test_cli_project_service_unit.py +26 -0
- tests/unit/cli/projects/test_cli_project_unit.py +19 -0
- tests/unit/cloudbender/test_datastores_unit.py +1 -5
- tests/unit/conftest.py +79 -6
- tests/unit/projects/test_project_credentials_unit.py +100 -0
- tests/unit/projects/test_projects_unit.py +1 -1
- tests/unit/test_checkpoints_unit.py +15 -23
- tests/unit/test_datasets_unit.py +15 -20
- tests/unit/test_models_unit.py +13 -16
- tests/unit/test_volumes_unit.py +3 -0
- trainml/__init__.py +1 -1
- trainml/checkpoints.py +14 -3
- trainml/cli/cloudbender/datastore.py +2 -7
- trainml/cli/job/create.py +16 -16
- trainml/cli/project/__init__.py +4 -73
- trainml/cli/project/credential.py +128 -0
- trainml/cli/project/data_connector.py +61 -0
- trainml/cli/project/datastore.py +61 -0
- trainml/cli/project/secret.py +12 -3
- trainml/cli/project/service.py +61 -0
- trainml/cloudbender/data_connectors.py +8 -0
- trainml/cloudbender/datastores.py +9 -19
- trainml/cloudbender/nodes.py +44 -1
- trainml/cloudbender/providers.py +53 -0
- trainml/cloudbender/regions.py +48 -0
- trainml/datasets.py +14 -3
- trainml/exceptions.py +51 -0
- trainml/jobs.py +2 -13
- trainml/models.py +14 -3
- trainml/projects/credentials.py +71 -0
- trainml/projects/projects.py +7 -4
- trainml/projects/secrets.py +1 -1
- trainml/volumes.py +15 -3
- {trainml-0.5.9.dist-info → trainml-0.5.12.dist-info}/METADATA +1 -1
- {trainml-0.5.9.dist-info → trainml-0.5.12.dist-info}/RECORD +53 -46
- tests/integration/test_projects_integration.py +0 -44
- tests/unit/cli/cloudbender/test_cli_reservation_unit.py +0 -34
- tests/unit/cli/test_cli_project_unit.py +0 -42
- tests/unit/cloudbender/test_reservations_unit.py +0 -173
- tests/unit/test_auth.py +0 -30
- tests/unit/test_projects_unit.py +0 -320
- tests/unit/test_trainml.py +0 -54
- trainml/cli/cloudbender/reservation.py +0 -159
- trainml/cli/project.py +0 -149
- trainml/cloudbender/reservations.py +0 -126
- trainml/projects.py +0 -228
- {trainml-0.5.9.dist-info → trainml-0.5.12.dist-info}/LICENSE +0 -0
- {trainml-0.5.9.dist-info → trainml-0.5.12.dist-info}/WHEEL +0 -0
- {trainml-0.5.9.dist-info → trainml-0.5.12.dist-info}/entry_points.txt +0 -0
- {trainml-0.5.9.dist-info → trainml-0.5.12.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,8 @@ from pytest import fixture
|
|
|
3
3
|
|
|
4
4
|
@fixture(scope="module")
|
|
5
5
|
async def project(trainml):
|
|
6
|
-
project = await trainml.projects.create(
|
|
6
|
+
project = await trainml.projects.create(
|
|
7
|
+
name="New Project", copy_credentials=False, copy_secrets=False
|
|
8
|
+
)
|
|
7
9
|
yield project
|
|
8
10
|
await project.remove()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
from pytest import mark, fixture
|
|
5
|
+
|
|
6
|
+
pytestmark = [mark.sdk, mark.integration, mark.projects]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@mark.create
|
|
10
|
+
@mark.asyncio
|
|
11
|
+
class ProjectCredentialsTests:
|
|
12
|
+
@fixture(scope="class")
|
|
13
|
+
async def project_credential(self, project):
|
|
14
|
+
project_credential = await project.credentials.put(
|
|
15
|
+
type="aws", key_id="ASFHALKF", secret="IUHKLHKAHF"
|
|
16
|
+
)
|
|
17
|
+
yield project_credential
|
|
18
|
+
await project.credentials.remove(type="aws")
|
|
19
|
+
|
|
20
|
+
async def test_list_project_credentials(self, project, project_credential):
|
|
21
|
+
credentials = await project.credentials.list()
|
|
22
|
+
assert len(credentials) > 0
|
|
23
|
+
|
|
24
|
+
async def test_project_credential_properties(self, project, project_credential):
|
|
25
|
+
assert isinstance(project_credential.project_uuid, str)
|
|
26
|
+
assert isinstance(project_credential.type, str)
|
|
27
|
+
assert isinstance(project_credential.key_id, str)
|
|
28
|
+
assert project_credential.type == "aws"
|
|
29
|
+
assert project.id == project_credential.project_uuid
|
|
30
|
+
|
|
31
|
+
async def test_project_credential_str(self, project_credential):
|
|
32
|
+
string = str(project_credential)
|
|
33
|
+
regex = r"^{.*\"type\": \"" + project_credential.type + r"\".*}$"
|
|
34
|
+
assert isinstance(string, str)
|
|
35
|
+
assert re.match(regex, string)
|
|
36
|
+
|
|
37
|
+
async def test_project_credential_repr(self, project_credential):
|
|
38
|
+
string = repr(project_credential)
|
|
39
|
+
regex = (
|
|
40
|
+
r"^ProjectCredential\( trainml , \*\*{.*'type': '"
|
|
41
|
+
+ project_credential.type
|
|
42
|
+
+ r"'.*}\)$"
|
|
43
|
+
)
|
|
44
|
+
assert isinstance(string, str)
|
|
45
|
+
assert re.match(regex, string)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
from pytest import mark, fixture
|
|
5
|
+
|
|
6
|
+
pytestmark = [mark.sdk, mark.integration, mark.projects]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@mark.create
|
|
10
|
+
@mark.asyncio
|
|
11
|
+
class ProjectDataConnectorsTests:
|
|
12
|
+
@fixture(scope="class")
|
|
13
|
+
async def project_data_connector(self, project):
|
|
14
|
+
data_connectors = await project.data_connectors.list()
|
|
15
|
+
yield data_connectors[0]
|
|
16
|
+
|
|
17
|
+
async def test_list_project_data_connectors(self, project, project_data_connector):
|
|
18
|
+
data_connectors = await project.data_connectors.list()
|
|
19
|
+
assert len(data_connectors) > 0
|
|
20
|
+
|
|
21
|
+
async def test_project_data_connector_properties(
|
|
22
|
+
self, project, project_data_connector
|
|
23
|
+
):
|
|
24
|
+
assert isinstance(project_data_connector.project_uuid, str)
|
|
25
|
+
assert isinstance(project_data_connector.type, str)
|
|
26
|
+
assert isinstance(project_data_connector.name, str)
|
|
27
|
+
assert isinstance(project_data_connector.region_uuid, str)
|
|
28
|
+
assert project.id == project_data_connector.project_uuid
|
|
29
|
+
|
|
30
|
+
async def test_project_data_connector_str(self, project_data_connector):
|
|
31
|
+
string = str(project_data_connector)
|
|
32
|
+
regex = r"^{.*\"name\": \"" + project_data_connector.name + r"\".*}$"
|
|
33
|
+
assert isinstance(string, str)
|
|
34
|
+
assert re.match(regex, string)
|
|
35
|
+
|
|
36
|
+
async def test_project_data_connector_repr(self, project_data_connector):
|
|
37
|
+
string = repr(project_data_connector)
|
|
38
|
+
regex = (
|
|
39
|
+
r"^ProjectDataConnector\( trainml , \*\*{.*'name': '"
|
|
40
|
+
+ project_data_connector.name
|
|
41
|
+
+ r"'.*}\)$"
|
|
42
|
+
)
|
|
43
|
+
assert isinstance(string, str)
|
|
44
|
+
assert re.match(regex, string)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
from pytest import mark, fixture
|
|
5
|
+
|
|
6
|
+
pytestmark = [mark.sdk, mark.integration, mark.projects]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@mark.create
|
|
10
|
+
@mark.asyncio
|
|
11
|
+
class ProjectDatastoresTests:
|
|
12
|
+
@fixture(scope="class")
|
|
13
|
+
async def project_datastore(self, project):
|
|
14
|
+
datastores = await project.datastores.list()
|
|
15
|
+
yield datastores[0]
|
|
16
|
+
|
|
17
|
+
async def test_list_project_datastores(self, project):
|
|
18
|
+
datastores = await project.datastores.list()
|
|
19
|
+
assert len(datastores) > 0
|
|
20
|
+
|
|
21
|
+
async def test_project_datastore_properties(self, project, project_datastore):
|
|
22
|
+
assert isinstance(project_datastore.project_uuid, str)
|
|
23
|
+
assert isinstance(project_datastore.type, str)
|
|
24
|
+
assert isinstance(project_datastore.name, str)
|
|
25
|
+
assert isinstance(project_datastore.region_uuid, str)
|
|
26
|
+
assert project.id == project_datastore.project_uuid
|
|
27
|
+
|
|
28
|
+
async def test_project_datastore_str(self, project_datastore):
|
|
29
|
+
string = str(project_datastore)
|
|
30
|
+
regex = r"^{.*\"name\": \"" + project_datastore.name + r"\".*}$"
|
|
31
|
+
assert isinstance(string, str)
|
|
32
|
+
assert re.match(regex, string)
|
|
33
|
+
|
|
34
|
+
async def test_project_datastore_repr(self, project_datastore):
|
|
35
|
+
string = repr(project_datastore)
|
|
36
|
+
regex = (
|
|
37
|
+
r"^ProjectDatastore\( trainml , \*\*{.*'name': '"
|
|
38
|
+
+ project_datastore.name
|
|
39
|
+
+ r"'.*}\)$"
|
|
40
|
+
)
|
|
41
|
+
assert isinstance(string, str)
|
|
42
|
+
assert re.match(regex, string)
|
|
@@ -17,7 +17,7 @@ class ProjectSecretsTests:
|
|
|
17
17
|
yield project_secret
|
|
18
18
|
await project.secrets.remove(name="secret_value")
|
|
19
19
|
|
|
20
|
-
async def
|
|
20
|
+
async def test_list_project_secrets(self, project, project_secret):
|
|
21
21
|
secrets = await project.secrets.list()
|
|
22
22
|
assert len(secrets) > 0
|
|
23
23
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
from pytest import mark, fixture
|
|
5
|
+
|
|
6
|
+
pytestmark = [mark.sdk, mark.integration, mark.projects]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@mark.create
|
|
10
|
+
@mark.asyncio
|
|
11
|
+
class ProjectServicesTests:
|
|
12
|
+
@fixture(scope="class")
|
|
13
|
+
async def project_service(self, project):
|
|
14
|
+
services = await project.services.list()
|
|
15
|
+
yield services[0]
|
|
16
|
+
|
|
17
|
+
async def test_list_project_services(self, project):
|
|
18
|
+
services = await project.services.list()
|
|
19
|
+
assert len(services) > 0
|
|
20
|
+
|
|
21
|
+
async def test_project_service_properties(self, project, project_service):
|
|
22
|
+
assert isinstance(project_service.project_uuid, str)
|
|
23
|
+
assert isinstance(project_service.name, str)
|
|
24
|
+
assert isinstance(project_service.id, str)
|
|
25
|
+
assert isinstance(project_service.hostname, str)
|
|
26
|
+
assert isinstance(project_service.region_uuid, str)
|
|
27
|
+
assert isinstance(project_service.public, bool)
|
|
28
|
+
assert project.id == project_service.project_uuid
|
|
29
|
+
|
|
30
|
+
async def test_project_service_str(self, project_service):
|
|
31
|
+
string = str(project_service)
|
|
32
|
+
regex = r"^{.*\"name\": \"" + project_service.name + r"\".*}$"
|
|
33
|
+
assert isinstance(string, str)
|
|
34
|
+
assert re.match(regex, string)
|
|
35
|
+
|
|
36
|
+
async def test_project_service_repr(self, project_service):
|
|
37
|
+
string = repr(project_service)
|
|
38
|
+
regex = (
|
|
39
|
+
r"^ProjectService\( trainml , \*\*{.*'name': '"
|
|
40
|
+
+ project_service.name
|
|
41
|
+
+ r"'.*}\)$"
|
|
42
|
+
)
|
|
43
|
+
assert isinstance(string, str)
|
|
44
|
+
assert re.match(regex, string)
|
|
@@ -16,7 +16,7 @@ class GetCheckpointTests:
|
|
|
16
16
|
source_type="git",
|
|
17
17
|
source_uri="git@github.com:trainML/environment-tests.git",
|
|
18
18
|
)
|
|
19
|
-
checkpoint = await checkpoint.wait_for("ready",
|
|
19
|
+
checkpoint = await checkpoint.wait_for("ready", 180)
|
|
20
20
|
yield checkpoint
|
|
21
21
|
await checkpoint.remove()
|
|
22
22
|
checkpoint = await checkpoint.wait_for("archived", 60)
|
|
@@ -59,7 +59,6 @@ async def test_checkpoint_wasabi(trainml, capsys):
|
|
|
59
59
|
name="CLI Automated Wasabi",
|
|
60
60
|
source_type="wasabi",
|
|
61
61
|
source_uri="s3://trainml-example/models/trainml-examples",
|
|
62
|
-
capacity="10G",
|
|
63
62
|
source_options=dict(endpoint_url="https://s3.wasabisys.com"),
|
|
64
63
|
)
|
|
65
64
|
checkpoint = await checkpoint.wait_for("ready", 300)
|
|
@@ -55,7 +55,6 @@ async def test_model_wasabi(trainml, capsys):
|
|
|
55
55
|
name="CLI Automated Wasabi",
|
|
56
56
|
source_type="wasabi",
|
|
57
57
|
source_uri="s3://trainml-example/models/trainml-examples",
|
|
58
|
-
capacity="10G",
|
|
59
58
|
source_options=dict(endpoint_url="https://s3.wasabisys.com"),
|
|
60
59
|
)
|
|
61
60
|
model = await model.wait_for("ready", 300)
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli.project import credential as specimen
|
|
10
|
+
from trainml.projects import (
|
|
11
|
+
Project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_credentials(runner, mock_project_credentials):
|
|
16
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
17
|
+
project = create_autospec(Project)
|
|
18
|
+
mock_trainml.projects = AsyncMock()
|
|
19
|
+
mock_trainml.projects.get = AsyncMock(return_value=project)
|
|
20
|
+
mock_trainml.projects.get_current = AsyncMock(return_value=project)
|
|
21
|
+
project.credentials = AsyncMock()
|
|
22
|
+
project.credentials.list = AsyncMock(return_value=mock_project_credentials)
|
|
23
|
+
result = runner.invoke(specimen, ["list"])
|
|
24
|
+
print(result)
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
project.credentials.list.assert_called_once()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli.project import data_connector as specimen
|
|
10
|
+
from trainml.projects import (
|
|
11
|
+
Project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_data_connectors(runner, mock_project_data_connectors):
|
|
16
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
17
|
+
project = create_autospec(Project)
|
|
18
|
+
mock_trainml.projects = AsyncMock()
|
|
19
|
+
mock_trainml.projects.get = AsyncMock(return_value=project)
|
|
20
|
+
mock_trainml.projects.get_current = AsyncMock(return_value=project)
|
|
21
|
+
project.data_connectors = AsyncMock()
|
|
22
|
+
project.data_connectors.list = AsyncMock(
|
|
23
|
+
return_value=mock_project_data_connectors
|
|
24
|
+
)
|
|
25
|
+
result = runner.invoke(specimen, ["list"])
|
|
26
|
+
print(result)
|
|
27
|
+
assert result.exit_code == 0
|
|
28
|
+
project.data_connectors.list.assert_called_once()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli.project import datastore as specimen
|
|
10
|
+
from trainml.projects import (
|
|
11
|
+
Project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_datastores(runner, mock_project_datastores):
|
|
16
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
17
|
+
project = create_autospec(Project)
|
|
18
|
+
mock_trainml.projects = AsyncMock()
|
|
19
|
+
mock_trainml.projects.get = AsyncMock(return_value=project)
|
|
20
|
+
mock_trainml.projects.get_current = AsyncMock(return_value=project)
|
|
21
|
+
project.datastores = AsyncMock()
|
|
22
|
+
project.datastores.list = AsyncMock(return_value=mock_project_datastores)
|
|
23
|
+
result = runner.invoke(specimen, ["list"])
|
|
24
|
+
print(result)
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
project.datastores.list.assert_called_once()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli.project import key as specimen
|
|
10
|
+
from trainml.projects import (
|
|
11
|
+
Project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_keys(runner, mock_project_keys):
|
|
16
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
17
|
+
project = create_autospec(Project)
|
|
18
|
+
mock_trainml.projects = AsyncMock()
|
|
19
|
+
mock_trainml.projects.get = AsyncMock(return_value=project)
|
|
20
|
+
mock_trainml.projects.get_current = AsyncMock(return_value=project)
|
|
21
|
+
project.keys = AsyncMock()
|
|
22
|
+
project.keys.list = AsyncMock(return_value=mock_project_keys)
|
|
23
|
+
result = runner.invoke(specimen, ["list"])
|
|
24
|
+
print(result)
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
project.keys.list.assert_called_once()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli.project import secret as specimen
|
|
10
|
+
from trainml.projects import (
|
|
11
|
+
Project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_secrets(runner, mock_project_secrets):
|
|
16
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
17
|
+
project = create_autospec(Project)
|
|
18
|
+
mock_trainml.projects = AsyncMock()
|
|
19
|
+
mock_trainml.projects.get = AsyncMock(return_value=project)
|
|
20
|
+
mock_trainml.projects.get_current = AsyncMock(return_value=project)
|
|
21
|
+
project.secrets = AsyncMock()
|
|
22
|
+
project.secrets.list = AsyncMock(return_value=mock_project_secrets)
|
|
23
|
+
result = runner.invoke(specimen, ["list"])
|
|
24
|
+
print(result)
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
project.secrets.list.assert_called_once()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli.project import service as specimen
|
|
10
|
+
from trainml.projects import (
|
|
11
|
+
Project,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_services(runner, mock_project_services):
|
|
16
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
17
|
+
project = create_autospec(Project)
|
|
18
|
+
mock_trainml.projects = AsyncMock()
|
|
19
|
+
mock_trainml.projects.get = AsyncMock(return_value=project)
|
|
20
|
+
mock_trainml.projects.get_current = AsyncMock(return_value=project)
|
|
21
|
+
project.services = AsyncMock()
|
|
22
|
+
project.services.list = AsyncMock(return_value=mock_project_services)
|
|
23
|
+
result = runner.invoke(specimen, ["list"])
|
|
24
|
+
print(result)
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
project.services.list.assert_called_once()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from unittest.mock import AsyncMock, patch, create_autospec
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.projects]
|
|
8
|
+
|
|
9
|
+
from trainml.cli import project as specimen
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_list(runner, mock_projects):
|
|
13
|
+
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
14
|
+
mock_trainml.projects = AsyncMock()
|
|
15
|
+
mock_trainml.projects.list = AsyncMock(return_value=mock_projects)
|
|
16
|
+
result = runner.invoke(specimen, ["list"])
|
|
17
|
+
print(result)
|
|
18
|
+
assert result.exit_code == 0
|
|
19
|
+
mock_trainml.projects.list.assert_called_once()
|
|
@@ -119,8 +119,6 @@ class datastoreTests:
|
|
|
119
119
|
assert isinstance(datastore.region_uuid, str)
|
|
120
120
|
assert isinstance(datastore.type, str)
|
|
121
121
|
assert isinstance(datastore.name, str)
|
|
122
|
-
assert isinstance(datastore.uri, str)
|
|
123
|
-
assert isinstance(datastore.root, str)
|
|
124
122
|
|
|
125
123
|
def test_datastore_str(self, datastore):
|
|
126
124
|
string = str(datastore)
|
|
@@ -131,9 +129,7 @@ class datastoreTests:
|
|
|
131
129
|
def test_datastore_repr(self, datastore):
|
|
132
130
|
string = repr(datastore)
|
|
133
131
|
regex = (
|
|
134
|
-
r"^Datastore\( trainml , \*\*{.*'store_id': '"
|
|
135
|
-
+ datastore.id
|
|
136
|
-
+ r"'.*}\)$"
|
|
132
|
+
r"^Datastore\( trainml , \*\*{.*'store_id': '" + datastore.id + r"'.*}\)$"
|
|
137
133
|
)
|
|
138
134
|
assert isinstance(string, str)
|
|
139
135
|
assert re.match(regex, string)
|
tests/unit/conftest.py
CHANGED
|
@@ -17,9 +17,11 @@ from trainml.projects import (
|
|
|
17
17
|
Projects,
|
|
18
18
|
Project,
|
|
19
19
|
)
|
|
20
|
-
from trainml.projects.datastores import ProjectDatastore
|
|
21
|
-
from trainml.projects.services import ProjectService
|
|
22
|
-
from trainml.projects.data_connectors import ProjectDataConnector
|
|
20
|
+
from trainml.projects.datastores import ProjectDatastores, ProjectDatastore
|
|
21
|
+
from trainml.projects.services import ProjectServices, ProjectService
|
|
22
|
+
from trainml.projects.data_connectors import ProjectDataConnectors, ProjectDataConnector
|
|
23
|
+
from trainml.projects.credentials import ProjectCredentials, ProjectCredential
|
|
24
|
+
from trainml.projects.secrets import ProjectSecrets, ProjectSecret
|
|
23
25
|
|
|
24
26
|
from trainml.cloudbender import Cloudbender
|
|
25
27
|
from trainml.cloudbender.providers import Provider, Providers
|
|
@@ -530,7 +532,7 @@ def mock_jobs():
|
|
|
530
532
|
{"value": "env1val", "key": "env1"},
|
|
531
533
|
{"value": "env2val", "key": "env2"},
|
|
532
534
|
],
|
|
533
|
-
"
|
|
535
|
+
"credentials": ["aws", "gcp"],
|
|
534
536
|
"status": "new",
|
|
535
537
|
},
|
|
536
538
|
"vpn": {
|
|
@@ -607,7 +609,7 @@ def mock_jobs():
|
|
|
607
609
|
"type": "DEEPLEARNING_PY37",
|
|
608
610
|
"image_size": 39656398629,
|
|
609
611
|
"env": [],
|
|
610
|
-
"
|
|
612
|
+
"credentials": [],
|
|
611
613
|
"status": "ready",
|
|
612
614
|
},
|
|
613
615
|
"vpn": {
|
|
@@ -1038,6 +1040,60 @@ def mock_device_configs():
|
|
|
1038
1040
|
]
|
|
1039
1041
|
|
|
1040
1042
|
|
|
1043
|
+
@fixture(
|
|
1044
|
+
scope="session",
|
|
1045
|
+
)
|
|
1046
|
+
def mock_project_credentials():
|
|
1047
|
+
trainml = Mock()
|
|
1048
|
+
yield [
|
|
1049
|
+
ProjectCredential(
|
|
1050
|
+
trainml,
|
|
1051
|
+
**{
|
|
1052
|
+
"project_uuid": "proj-id-1",
|
|
1053
|
+
"type": "aws",
|
|
1054
|
+
"key_id": "AKSHFKHDFS",
|
|
1055
|
+
"updatedAt": "2023-06-02T21:22:40.084Z",
|
|
1056
|
+
},
|
|
1057
|
+
),
|
|
1058
|
+
ProjectCredential(
|
|
1059
|
+
trainml,
|
|
1060
|
+
**{
|
|
1061
|
+
"project_uuid": "proj-id-1",
|
|
1062
|
+
"type": "gcp",
|
|
1063
|
+
"key_id": "credentials.json",
|
|
1064
|
+
"updatedAt": "2023-06-02T21:22:40.084Z",
|
|
1065
|
+
},
|
|
1066
|
+
),
|
|
1067
|
+
]
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
@fixture(
|
|
1071
|
+
scope="session",
|
|
1072
|
+
)
|
|
1073
|
+
def mock_project_secrets():
|
|
1074
|
+
trainml = Mock()
|
|
1075
|
+
yield [
|
|
1076
|
+
ProjectSecret(
|
|
1077
|
+
trainml,
|
|
1078
|
+
**{
|
|
1079
|
+
"project_uuid": "proj-id-1",
|
|
1080
|
+
"name": "super_secret",
|
|
1081
|
+
"created_by": "User",
|
|
1082
|
+
"updatedAt": "2023-06-02T21:22:40.084Z",
|
|
1083
|
+
},
|
|
1084
|
+
),
|
|
1085
|
+
ProjectSecret(
|
|
1086
|
+
trainml,
|
|
1087
|
+
**{
|
|
1088
|
+
"project_uuid": "proj-id-1",
|
|
1089
|
+
"name": "super_secret_2",
|
|
1090
|
+
"created_by": "User",
|
|
1091
|
+
"updatedAt": "2023-06-02T21:22:40.084Z",
|
|
1092
|
+
},
|
|
1093
|
+
),
|
|
1094
|
+
]
|
|
1095
|
+
|
|
1096
|
+
|
|
1041
1097
|
@fixture(scope="function")
|
|
1042
1098
|
def mock_trainml(
|
|
1043
1099
|
mock_my_datasets,
|
|
@@ -1058,6 +1114,11 @@ def mock_trainml(
|
|
|
1058
1114
|
mock_services,
|
|
1059
1115
|
mock_data_connectors,
|
|
1060
1116
|
mock_device_configs,
|
|
1117
|
+
mock_project_datastores,
|
|
1118
|
+
mock_project_services,
|
|
1119
|
+
mock_project_data_connectors,
|
|
1120
|
+
mock_project_credentials,
|
|
1121
|
+
mock_project_secrets,
|
|
1061
1122
|
):
|
|
1062
1123
|
trainml = create_autospec(TrainML)
|
|
1063
1124
|
trainml.active_project = "proj-id-1"
|
|
@@ -1081,6 +1142,18 @@ def mock_trainml(
|
|
|
1081
1142
|
trainml.environments.list = AsyncMock(return_value=mock_environments)
|
|
1082
1143
|
trainml.jobs.list = AsyncMock(return_value=mock_jobs)
|
|
1083
1144
|
trainml.projects.list = AsyncMock(return_value=mock_projects)
|
|
1145
|
+
trainml.projects.datastores = create_autospec(ProjectDatastores)
|
|
1146
|
+
trainml.projects.datastores.list = AsyncMock(return_value=mock_project_datastores)
|
|
1147
|
+
trainml.projects.services = create_autospec(ProjectServices)
|
|
1148
|
+
trainml.projects.services.list = AsyncMock(return_value=mock_project_services)
|
|
1149
|
+
trainml.projects.data_connectors = create_autospec(ProjectDataConnectors)
|
|
1150
|
+
trainml.projects.data_connectors.list = AsyncMock(
|
|
1151
|
+
return_value=mock_project_data_connectors
|
|
1152
|
+
)
|
|
1153
|
+
trainml.projects.credentials = create_autospec(ProjectCredentials)
|
|
1154
|
+
trainml.projects.credentials.list = AsyncMock(return_value=mock_project_credentials)
|
|
1155
|
+
trainml.projects.secrets = create_autospec(ProjectSecrets)
|
|
1156
|
+
trainml.projects.secrets.list = AsyncMock(return_value=mock_project_secrets)
|
|
1084
1157
|
|
|
1085
1158
|
trainml.cloudbender = create_autospec(Cloudbender)
|
|
1086
1159
|
|
|
@@ -1090,7 +1163,7 @@ def mock_trainml(
|
|
|
1090
1163
|
trainml.cloudbender.regions.list = AsyncMock(return_value=mock_regions)
|
|
1091
1164
|
trainml.cloudbender.nodes = create_autospec(Nodes)
|
|
1092
1165
|
trainml.cloudbender.nodes.list = AsyncMock(return_value=mock_nodes)
|
|
1093
|
-
trainml.cloudbender.devices = create_autospec(
|
|
1166
|
+
trainml.cloudbender.devices = create_autospec(Devices)
|
|
1094
1167
|
trainml.cloudbender.devices.list = AsyncMock(return_value=mock_devices)
|
|
1095
1168
|
trainml.cloudbender.datastores = create_autospec(Datastores)
|
|
1096
1169
|
trainml.cloudbender.datastores.list = AsyncMock(return_value=mock_datastores)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from unittest.mock import AsyncMock, patch
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
from aiohttp import WSMessage, WSMsgType
|
|
7
|
+
|
|
8
|
+
import trainml.projects.credentials as specimen
|
|
9
|
+
from trainml.exceptions import (
|
|
10
|
+
ApiError,
|
|
11
|
+
SpecificationError,
|
|
12
|
+
TrainMLException,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
pytestmark = [mark.sdk, mark.unit, mark.projects]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@fixture
|
|
19
|
+
def project_credentials(mock_trainml):
|
|
20
|
+
yield specimen.ProjectCredentials(mock_trainml, project_id="1")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@fixture
|
|
24
|
+
def project_credential(mock_trainml):
|
|
25
|
+
yield specimen.ProjectCredential(
|
|
26
|
+
mock_trainml, project_uuid="proj-id-1", type="aws", key_id="AIYHGFSDLK"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ProjectCredentialsTests:
|
|
31
|
+
@mark.asyncio
|
|
32
|
+
async def test_project_credentials_list(self, project_credentials, mock_trainml):
|
|
33
|
+
api_response = [
|
|
34
|
+
{"project_uuid": "proj-id-1", "type": "aws", "key_id": "AIYHGFSDLK"},
|
|
35
|
+
{"project_uuid": "proj-id-1", "type": "gcp", "key_id": "credentials.json"},
|
|
36
|
+
]
|
|
37
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
38
|
+
resp = await project_credentials.list()
|
|
39
|
+
mock_trainml._query.assert_called_once_with(
|
|
40
|
+
"/project/1/credentials", "GET", dict()
|
|
41
|
+
)
|
|
42
|
+
assert len(resp) == 2
|
|
43
|
+
|
|
44
|
+
@mark.asyncio
|
|
45
|
+
async def test_remove_project_credential(
|
|
46
|
+
self,
|
|
47
|
+
project_credentials,
|
|
48
|
+
mock_trainml,
|
|
49
|
+
):
|
|
50
|
+
api_response = dict()
|
|
51
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
52
|
+
await project_credentials.remove("aws")
|
|
53
|
+
mock_trainml._query.assert_called_once_with(
|
|
54
|
+
"/project/1/credential/aws", "DELETE", dict()
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@mark.asyncio
|
|
58
|
+
async def test_put_project_credential(self, project_credentials, mock_trainml):
|
|
59
|
+
requested_config = dict(type="aws", key_id="AIUDHADA", secret="ASKHJSLKF")
|
|
60
|
+
expected_payload = dict(key_id="AIUDHADA", secret="ASKHJSLKF")
|
|
61
|
+
api_response = {
|
|
62
|
+
"project_uuid": "project-id-1",
|
|
63
|
+
"type": "aws",
|
|
64
|
+
"key_id": "AIUDHADA",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
68
|
+
response = await project_credentials.put(**requested_config)
|
|
69
|
+
mock_trainml._query.assert_called_once_with(
|
|
70
|
+
"/project/1/credential/aws", "PUT", None, expected_payload
|
|
71
|
+
)
|
|
72
|
+
assert response.type == "aws"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ProjectCredentialTests:
|
|
76
|
+
def test_project_credential_properties(self, project_credential):
|
|
77
|
+
assert isinstance(project_credential.type, str)
|
|
78
|
+
assert isinstance(project_credential.key_id, str)
|
|
79
|
+
assert isinstance(project_credential.project_uuid, str)
|
|
80
|
+
|
|
81
|
+
def test_project_credential_str(self, project_credential):
|
|
82
|
+
string = str(project_credential)
|
|
83
|
+
regex = r"^{.*\"type\": \"" + project_credential.type + r"\".*}$"
|
|
84
|
+
assert isinstance(string, str)
|
|
85
|
+
assert re.match(regex, string)
|
|
86
|
+
|
|
87
|
+
def test_project_credential_repr(self, project_credential):
|
|
88
|
+
string = repr(project_credential)
|
|
89
|
+
regex = (
|
|
90
|
+
r"^ProjectCredential\( trainml , \*\*{.*'type': '"
|
|
91
|
+
+ project_credential.type
|
|
92
|
+
+ r"'.*}\)$"
|
|
93
|
+
)
|
|
94
|
+
assert isinstance(string, str)
|
|
95
|
+
assert re.match(regex, string)
|
|
96
|
+
|
|
97
|
+
def test_project_credential_bool(self, project_credential, mock_trainml):
|
|
98
|
+
empty_project_credential = specimen.ProjectCredential(mock_trainml)
|
|
99
|
+
assert bool(project_credential)
|
|
100
|
+
assert not bool(empty_project_credential)
|