trainml 0.5.13__py3-none-any.whl → 0.5.15__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/cloudbender/conftest.py +28 -0
- tests/integration/cloudbender/test_providers_integration.py +3 -8
- tests/integration/cloudbender/test_regions_integration.py +42 -0
- tests/integration/cloudbender/test_services_integration.py +87 -0
- tests/integration/conftest.py +1 -1
- tests/integration/projects/conftest.py +2 -1
- tests/integration/projects/test_projects_credentials_integration.py +1 -0
- tests/integration/projects/test_projects_data_connectors_integration.py +1 -0
- tests/integration/projects/test_projects_datastores_integration.py +1 -0
- tests/integration/projects/test_projects_integration.py +1 -0
- tests/integration/projects/test_projects_members_integration.py +50 -0
- tests/integration/projects/test_projects_secrets_integration.py +1 -0
- tests/integration/projects/test_projects_services_integration.py +1 -0
- tests/integration/test_checkpoints_integration.py +1 -0
- tests/integration/test_datasets_integration.py +1 -0
- tests/integration/test_jobs_integration.py +13 -8
- tests/integration/test_models_integration.py +1 -0
- tests/integration/test_volumes_integration.py +1 -0
- tests/unit/projects/test_project_members_unit.py +107 -0
- trainml/__init__.py +1 -1
- trainml/auth.py +1 -1
- trainml/cli/job/create.py +32 -2
- trainml/cloudbender/services.py +19 -1
- trainml/jobs.py +9 -0
- trainml/projects/members.py +98 -0
- trainml/projects/projects.py +2 -0
- trainml/projects/services.py +17 -0
- trainml/trainml.py +37 -20
- {trainml-0.5.13.dist-info → trainml-0.5.15.dist-info}/METADATA +1 -1
- {trainml-0.5.13.dist-info → trainml-0.5.15.dist-info}/RECORD +34 -33
- tests/integration/projects/test_projects_keys_integration.py +0 -43
- tests/unit/cli/projects/test_cli_project_key_unit.py +0 -26
- tests/unit/projects/test_project_keys_unit.py +0 -96
- trainml/cli/project/key.py +0 -124
- trainml/projects/keys.py +0 -71
- {trainml-0.5.13.dist-info → trainml-0.5.15.dist-info}/LICENSE +0 -0
- {trainml-0.5.13.dist-info → trainml-0.5.15.dist-info}/WHEEL +0 -0
- {trainml-0.5.13.dist-info → trainml-0.5.15.dist-info}/entry_points.txt +0 -0
- {trainml-0.5.13.dist-info → trainml-0.5.15.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
from pytest import fixture, mark
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
pytestmark = [mark.integration,mark.cloudbender]
|
|
6
|
+
|
|
7
|
+
@fixture(scope="session")
|
|
8
|
+
@mark.create
|
|
9
|
+
@mark.asyncio
|
|
10
|
+
@mark.xdist_group("cloudbender_resources")
|
|
11
|
+
async def provider( trainml):
|
|
12
|
+
provider = await trainml.cloudbender.providers.enable(type="test")
|
|
13
|
+
await provider.wait_for("ready")
|
|
14
|
+
yield provider
|
|
15
|
+
await provider.remove()
|
|
16
|
+
|
|
17
|
+
@fixture(scope="session")
|
|
18
|
+
@mark.create
|
|
19
|
+
@mark.asyncio
|
|
20
|
+
@mark.xdist_group("cloudbender_resources")
|
|
21
|
+
async def region(trainml, provider):
|
|
22
|
+
region = await trainml.cloudbender.regions.create(provider_uuid=provider.id,name="test-region",
|
|
23
|
+
public=False,
|
|
24
|
+
storage=dict(mode="local"),)
|
|
25
|
+
await region.wait_for("healthy")
|
|
26
|
+
yield region
|
|
27
|
+
await region.remove()
|
|
28
|
+
await region.wait_for("archived")
|
|
@@ -8,14 +8,9 @@ pytestmark = [mark.sdk, mark.integration, mark.cloudbender, mark.providers]
|
|
|
8
8
|
|
|
9
9
|
@mark.create
|
|
10
10
|
@mark.asyncio
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def
|
|
14
|
-
provider = await trainml.cloudbender.providers.enable(type="test")
|
|
15
|
-
yield provider
|
|
16
|
-
await provider.remove()
|
|
17
|
-
|
|
18
|
-
async def test_get_providers(self, trainml):
|
|
11
|
+
@mark.xdist_group("cloudbender_resources")
|
|
12
|
+
class GetProviderTests:
|
|
13
|
+
async def test_get_providers(self, trainml, provider):
|
|
19
14
|
providers = await trainml.cloudbender.providers.list()
|
|
20
15
|
assert len(providers) > 0
|
|
21
16
|
|
|
@@ -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.cloudbender, mark.regions]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@mark.create
|
|
10
|
+
@mark.asyncio
|
|
11
|
+
@mark.xdist_group("cloudbender_resources")
|
|
12
|
+
class GetRegionTests:
|
|
13
|
+
async def test_get_regions(self, trainml, provider, region):
|
|
14
|
+
regions = await trainml.cloudbender.regions.list(provider_uuid=provider.id)
|
|
15
|
+
assert len(regions) > 0
|
|
16
|
+
|
|
17
|
+
async def test_get_region(self, trainml, provider, region):
|
|
18
|
+
response = await trainml.cloudbender.regions.get(provider.id, region.id)
|
|
19
|
+
assert response.id == region.id
|
|
20
|
+
|
|
21
|
+
async def test_region_properties(self, provider, region):
|
|
22
|
+
assert isinstance(region.id, str)
|
|
23
|
+
assert isinstance(region.provider_uuid, str)
|
|
24
|
+
assert isinstance(region.type, str)
|
|
25
|
+
assert region.type == "test"
|
|
26
|
+
assert region.provider_uuid == provider.id
|
|
27
|
+
|
|
28
|
+
async def test_region_str(self, region):
|
|
29
|
+
string = str(region)
|
|
30
|
+
regex = r"^{.*\"region_uuid\": \"" + region.id + r"\".*}$"
|
|
31
|
+
assert isinstance(string, str)
|
|
32
|
+
assert re.match(regex, string)
|
|
33
|
+
|
|
34
|
+
async def test_region_repr(self, region):
|
|
35
|
+
string = repr(region)
|
|
36
|
+
regex = (
|
|
37
|
+
r"^Region\( trainml , \*\*{.*'region_uuid': '"
|
|
38
|
+
+ region.id
|
|
39
|
+
+ r"'.*}\)$"
|
|
40
|
+
)
|
|
41
|
+
assert isinstance(string, str)
|
|
42
|
+
assert re.match(regex, string)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
from pytest import mark, fixture
|
|
5
|
+
from cryptography import x509
|
|
6
|
+
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
|
7
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
8
|
+
from cryptography.hazmat.primitives import serialization, hashes
|
|
9
|
+
|
|
10
|
+
pytestmark = [mark.sdk, mark.integration, mark.cloudbender, mark.regions]
|
|
11
|
+
|
|
12
|
+
def get_csr(service_id):
|
|
13
|
+
private_key = rsa.generate_private_key(
|
|
14
|
+
public_exponent=65537,
|
|
15
|
+
key_size=4096
|
|
16
|
+
)
|
|
17
|
+
csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
|
|
18
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
|
19
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "proxiML"),
|
|
20
|
+
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, service_id),
|
|
21
|
+
x509.NameAttribute(NameOID.COMMON_NAME, "test-client"), # Client identity
|
|
22
|
+
])).add_extension(
|
|
23
|
+
x509.ExtendedKeyUsage([
|
|
24
|
+
ExtendedKeyUsageOID.CLIENT_AUTH # Client authentication usage
|
|
25
|
+
]),
|
|
26
|
+
critical=True
|
|
27
|
+
).sign(private_key, hashes.SHA256())
|
|
28
|
+
return csr.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
|
29
|
+
|
|
30
|
+
@mark.create
|
|
31
|
+
@mark.asyncio
|
|
32
|
+
@mark.xdist_group("cloudbender_resources")
|
|
33
|
+
class GetServiceTests:
|
|
34
|
+
@fixture(scope="class")
|
|
35
|
+
async def service(self, trainml, region):
|
|
36
|
+
service = await trainml.cloudbender.services.create(
|
|
37
|
+
provider_uuid=region.provider_uuid,
|
|
38
|
+
region_uuid=region.id,
|
|
39
|
+
name="CLI Automated Service",
|
|
40
|
+
type="tcp",
|
|
41
|
+
port="8989",
|
|
42
|
+
public=False,
|
|
43
|
+
)
|
|
44
|
+
await service.wait_for("active")
|
|
45
|
+
yield service
|
|
46
|
+
await service.remove()
|
|
47
|
+
await service.wait_for("archived")
|
|
48
|
+
|
|
49
|
+
async def test_get_services(self, trainml, region,service):
|
|
50
|
+
services = await trainml.cloudbender.services.list(provider_uuid=region.provider_uuid, region_uuid=region.id)
|
|
51
|
+
assert len(services) > 0
|
|
52
|
+
|
|
53
|
+
async def test_get_service(self, trainml, provider, region, service):
|
|
54
|
+
response = await trainml.cloudbender.services.get(provider.id, region.id, service.id)
|
|
55
|
+
assert response.id == service.id
|
|
56
|
+
|
|
57
|
+
async def test_service_properties(self, region, service):
|
|
58
|
+
assert isinstance(service.id, str)
|
|
59
|
+
assert isinstance(service.provider_uuid, str)
|
|
60
|
+
assert isinstance(service.region_uuid, str)
|
|
61
|
+
assert isinstance(service.public, bool)
|
|
62
|
+
assert service.port == "8989"
|
|
63
|
+
assert service.provider_uuid == region.provider_uuid
|
|
64
|
+
assert service.region_uuid == region.id
|
|
65
|
+
|
|
66
|
+
async def test_service_str(self, service):
|
|
67
|
+
string = str(service)
|
|
68
|
+
regex = r"^{.*\"service_id\": \"" + service.id + r"\".*}$"
|
|
69
|
+
assert isinstance(string, str)
|
|
70
|
+
assert re.match(regex, string)
|
|
71
|
+
|
|
72
|
+
async def test_service_repr(self, service):
|
|
73
|
+
string = repr(service)
|
|
74
|
+
regex = (
|
|
75
|
+
r"^Service\( trainml , \*\*{.*'service_id': '"
|
|
76
|
+
+ service.id
|
|
77
|
+
+ r"'.*}\)$"
|
|
78
|
+
)
|
|
79
|
+
assert isinstance(string, str)
|
|
80
|
+
assert re.match(regex, string)
|
|
81
|
+
|
|
82
|
+
async def test_service_certificate(self, service):
|
|
83
|
+
service = await service.generate_certificate()
|
|
84
|
+
assert isinstance(service._service.get("auth_cert"), str)
|
|
85
|
+
csr = get_csr(service.id)
|
|
86
|
+
certificate = await service.sign_client_certificate(csr)
|
|
87
|
+
assert isinstance(certificate, str)
|
tests/integration/conftest.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from pytest import fixture
|
|
1
|
+
from pytest import fixture, mark
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
@fixture(scope="module")
|
|
5
|
+
@mark.xdist_group("project_resources")
|
|
5
6
|
async def project(trainml):
|
|
6
7
|
project = await trainml.projects.create(
|
|
7
8
|
name="New Project", copy_credentials=False, copy_secrets=False
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
@mark.xdist_group("project_resources")
|
|
12
|
+
class ProjectMembersTests:
|
|
13
|
+
@fixture(scope="class")
|
|
14
|
+
async def project_member(self, project):
|
|
15
|
+
member = await project.members.add("test.account@proximl.ai","read","read","read","read","read")
|
|
16
|
+
yield member
|
|
17
|
+
await project.members.remove("test.account@proximl.ai")
|
|
18
|
+
|
|
19
|
+
async def test_list_project_members(self, project):
|
|
20
|
+
members = await project.members.list()
|
|
21
|
+
assert len(members) > 0
|
|
22
|
+
|
|
23
|
+
async def test_project_member_properties(self, project, project_member):
|
|
24
|
+
assert isinstance(project_member.id, str)
|
|
25
|
+
assert isinstance(project_member.email, str)
|
|
26
|
+
assert isinstance(project_member.project_uuid, str)
|
|
27
|
+
assert isinstance(project_member.owner, bool)
|
|
28
|
+
assert isinstance(project_member.job, str)
|
|
29
|
+
assert isinstance(project_member.dataset, str)
|
|
30
|
+
assert isinstance(project_member.model, str)
|
|
31
|
+
assert isinstance(project_member.checkpoint, str)
|
|
32
|
+
assert isinstance(project_member.volume, str)
|
|
33
|
+
assert project.id == project_member.project_uuid
|
|
34
|
+
assert project_member.id == "test.account@proximl.ai"
|
|
35
|
+
|
|
36
|
+
async def test_project_member_str(self, project_member):
|
|
37
|
+
string = str(project_member)
|
|
38
|
+
regex = r"^{.*\"email\": \"" + project_member.email + r"\".*}$"
|
|
39
|
+
assert isinstance(string, str)
|
|
40
|
+
assert re.match(regex, string)
|
|
41
|
+
|
|
42
|
+
async def test_project_member_repr(self, project_member):
|
|
43
|
+
string = repr(project_member)
|
|
44
|
+
regex = (
|
|
45
|
+
r"^ProjectMember\( trainml , \*\*{.*'email': '"
|
|
46
|
+
+ project_member.email
|
|
47
|
+
+ r"'.*}\)$"
|
|
48
|
+
)
|
|
49
|
+
assert isinstance(string, str)
|
|
50
|
+
assert re.match(regex, string)
|
|
@@ -20,6 +20,7 @@ def extract_domain_suffix(hostname):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@fixture(scope="class")
|
|
23
|
+
@mark.xdist_group("job_lifecycle")
|
|
23
24
|
async def job(trainml):
|
|
24
25
|
job = await trainml.jobs.create(
|
|
25
26
|
name="CLI Automated Tests - Job Lifecycle",
|
|
@@ -38,6 +39,7 @@ async def job(trainml):
|
|
|
38
39
|
|
|
39
40
|
@mark.create
|
|
40
41
|
@mark.asyncio
|
|
42
|
+
@mark.xdist_group("job_lifecycle")
|
|
41
43
|
class JobLifeCycleTests:
|
|
42
44
|
async def test_wait_for_running(self, job):
|
|
43
45
|
assert job.status != "running"
|
|
@@ -413,6 +415,7 @@ class JobAPIWorkerValidationTests:
|
|
|
413
415
|
|
|
414
416
|
@mark.create
|
|
415
417
|
@mark.asyncio
|
|
418
|
+
@mark.xdist_group("job_io")
|
|
416
419
|
class JobIOTests:
|
|
417
420
|
async def test_job_local_output(self, trainml, capsys):
|
|
418
421
|
temp_dir = tempfile.TemporaryDirectory()
|
|
@@ -423,11 +426,11 @@ class JobIOTests:
|
|
|
423
426
|
disk_size=10,
|
|
424
427
|
workers=["python $ML_MODEL_PATH/tensorflow/main.py"],
|
|
425
428
|
environment=dict(
|
|
426
|
-
type="
|
|
429
|
+
type="DEEPLEARNING_PY312",
|
|
427
430
|
env=[
|
|
428
431
|
dict(
|
|
429
432
|
key="CHECKPOINT_FILE",
|
|
430
|
-
value="model.ckpt-0050",
|
|
433
|
+
value="model.ckpt-0050.weights.h5",
|
|
431
434
|
)
|
|
432
435
|
],
|
|
433
436
|
),
|
|
@@ -469,7 +472,7 @@ class JobIOTests:
|
|
|
469
472
|
sys.stderr.write(captured.err)
|
|
470
473
|
assert "Epoch 1/2" in captured.out
|
|
471
474
|
assert "Epoch 2/2" in captured.out
|
|
472
|
-
assert "adding: model.ckpt-0001
|
|
475
|
+
assert "adding: model.ckpt-0001" in captured.out
|
|
473
476
|
assert "Send complete" in captured.out
|
|
474
477
|
|
|
475
478
|
async def test_job_model_input_and_output(self, trainml, capsys):
|
|
@@ -527,6 +530,7 @@ class JobIOTests:
|
|
|
527
530
|
|
|
528
531
|
@mark.create
|
|
529
532
|
@mark.asyncio
|
|
533
|
+
@mark.xdist_group("job_types")
|
|
530
534
|
class JobTypeTests:
|
|
531
535
|
async def test_endpoint(self, trainml):
|
|
532
536
|
|
|
@@ -558,7 +562,7 @@ class JobTypeTests:
|
|
|
558
562
|
assert job.url
|
|
559
563
|
assert extract_domain_suffix(urlparse(job.url).hostname) == "proximl.cloud"
|
|
560
564
|
tries = 0
|
|
561
|
-
await asyncio.sleep(
|
|
565
|
+
await asyncio.sleep(180) ## downloading weights can be slow
|
|
562
566
|
async with aiohttp.ClientSession() as session:
|
|
563
567
|
retry = True
|
|
564
568
|
while retry:
|
|
@@ -640,7 +644,7 @@ class JobTypeTests:
|
|
|
640
644
|
assert "Epoch 2/2" in captured.out
|
|
641
645
|
assert "Uploading s3://trainml-example/output/resnet_cifar10" in captured.out
|
|
642
646
|
assert (
|
|
643
|
-
"upload: ./model.ckpt-0002.
|
|
647
|
+
"upload: ./model.ckpt-0002.weights.h5 to s3://trainml-example/output/resnet_cifar10/model.ckpt-0002.weights.h5"
|
|
644
648
|
in captured.out
|
|
645
649
|
)
|
|
646
650
|
assert "Upload complete" in captured.out
|
|
@@ -648,6 +652,7 @@ class JobTypeTests:
|
|
|
648
652
|
|
|
649
653
|
@mark.create
|
|
650
654
|
@mark.asyncio
|
|
655
|
+
@mark.xdist_group("job_features")
|
|
651
656
|
class JobFeatureTests:
|
|
652
657
|
async def test_cpu_instance(self, trainml, capsys):
|
|
653
658
|
job = await trainml.jobs.create(
|
|
@@ -713,9 +718,9 @@ class JobFeatureTests:
|
|
|
713
718
|
sys.stderr.write(captured.err)
|
|
714
719
|
upload_contents = os.listdir(temp_dir.name)
|
|
715
720
|
temp_dir.cleanup()
|
|
716
|
-
assert len(upload_contents)
|
|
721
|
+
assert len(upload_contents) >= 3
|
|
717
722
|
assert any(
|
|
718
|
-
"model.ckpt-0002
|
|
723
|
+
"model.ckpt-0002" in content
|
|
719
724
|
for content in upload_contents
|
|
720
725
|
)
|
|
721
726
|
|
|
@@ -724,5 +729,5 @@ class JobFeatureTests:
|
|
|
724
729
|
sys.stderr.write(captured.err)
|
|
725
730
|
assert "Epoch 1/2" in captured.out
|
|
726
731
|
assert "Epoch 2/2" in captured.out
|
|
727
|
-
assert "Number of regular files transferred:
|
|
732
|
+
assert "Number of regular files transferred: 4" in captured.out
|
|
728
733
|
assert "Send complete" in captured.out
|
|
@@ -0,0 +1,107 @@
|
|
|
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.members 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_members(mock_trainml):
|
|
20
|
+
yield specimen.ProjectMembers(mock_trainml, project_id="1")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@fixture
|
|
24
|
+
def project_member(mock_trainml):
|
|
25
|
+
yield specimen.ProjectMember(
|
|
26
|
+
mock_trainml,
|
|
27
|
+
id="owner@gmail.com",
|
|
28
|
+
email="owner@gmail.com",
|
|
29
|
+
project_uuid="proj-id-1",
|
|
30
|
+
owner= True,
|
|
31
|
+
job= "all",
|
|
32
|
+
dataset= "all",
|
|
33
|
+
model= "all",
|
|
34
|
+
checkpoint="all",
|
|
35
|
+
volume= "all"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ProjectMembersTests:
|
|
40
|
+
@mark.asyncio
|
|
41
|
+
async def test_project_members_list(self, project_members, mock_trainml):
|
|
42
|
+
api_response = [
|
|
43
|
+
{
|
|
44
|
+
"project_uuid": "proj-id-1",
|
|
45
|
+
"email": "owner@gmail.com",
|
|
46
|
+
"createdAt": "2024-09-04T00:42:39.529Z",
|
|
47
|
+
"updatedAt": "2024-09-04T00:42:39.529Z",
|
|
48
|
+
"owner": True,
|
|
49
|
+
"job": "all",
|
|
50
|
+
"dataset": "all",
|
|
51
|
+
"model": "all",
|
|
52
|
+
"checkpoint": "all",
|
|
53
|
+
"volume": "all"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"project_uuid": "proj-id-1",
|
|
57
|
+
"email": "non-owner@gmail.com",
|
|
58
|
+
"createdAt": "2024-09-04T00:42:39.529Z",
|
|
59
|
+
"updatedAt": "2024-09-04T00:42:39.529Z",
|
|
60
|
+
"owner": False,
|
|
61
|
+
"job": "all",
|
|
62
|
+
"dataset": "all",
|
|
63
|
+
"model": "all",
|
|
64
|
+
"checkpoint": "read",
|
|
65
|
+
"volume": "read"
|
|
66
|
+
},
|
|
67
|
+
]
|
|
68
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
69
|
+
resp = await project_members.list()
|
|
70
|
+
mock_trainml._query.assert_called_once_with(
|
|
71
|
+
"/project/1/access", "GET", dict()
|
|
72
|
+
)
|
|
73
|
+
assert len(resp) == 2
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ProjectMemberTests:
|
|
77
|
+
def test_project_member_properties(self, project_member):
|
|
78
|
+
assert isinstance(project_member.id, str)
|
|
79
|
+
assert isinstance(project_member.email, str)
|
|
80
|
+
assert isinstance(project_member.project_uuid, str)
|
|
81
|
+
assert isinstance(project_member.owner, bool)
|
|
82
|
+
assert isinstance(project_member.job, str)
|
|
83
|
+
assert isinstance(project_member.dataset, str)
|
|
84
|
+
assert isinstance(project_member.model, str)
|
|
85
|
+
assert isinstance(project_member.checkpoint, str)
|
|
86
|
+
assert isinstance(project_member.volume, str)
|
|
87
|
+
|
|
88
|
+
def test_project_member_str(self, project_member):
|
|
89
|
+
string = str(project_member)
|
|
90
|
+
regex = r"^{.*\"id\": \"" + project_member.id + r"\".*}$"
|
|
91
|
+
assert isinstance(string, str)
|
|
92
|
+
assert re.match(regex, string)
|
|
93
|
+
|
|
94
|
+
def test_project_member_repr(self, project_member):
|
|
95
|
+
string = repr(project_member)
|
|
96
|
+
regex = (
|
|
97
|
+
r"^ProjectMember\( trainml , \*\*{.*'id': '"
|
|
98
|
+
+ project_member.id
|
|
99
|
+
+ r"'.*}\)$"
|
|
100
|
+
)
|
|
101
|
+
assert isinstance(string, str)
|
|
102
|
+
assert re.match(regex, string)
|
|
103
|
+
|
|
104
|
+
def test_project_member_bool(self, project_member, mock_trainml):
|
|
105
|
+
empty_project_member = specimen.ProjectMember(mock_trainml)
|
|
106
|
+
assert bool(project_member)
|
|
107
|
+
assert not bool(empty_project_member)
|
trainml/__init__.py
CHANGED
trainml/auth.py
CHANGED
|
@@ -508,7 +508,7 @@ class AWSSRP(object):
|
|
|
508
508
|
|
|
509
509
|
|
|
510
510
|
class Auth(object):
|
|
511
|
-
def __init__(self, config_dir, domain_suffix="
|
|
511
|
+
def __init__(self, config_dir, domain_suffix="proximl.ai", **kwargs):
|
|
512
512
|
try:
|
|
513
513
|
with open(f"{config_dir}/environment.json", "r") as file:
|
|
514
514
|
env_str = file.read().replace("\n", "")
|
trainml/cli/job/create.py
CHANGED
|
@@ -180,6 +180,12 @@ def create(config):
|
|
|
180
180
|
help="Third Party Credentials to add to the job environment",
|
|
181
181
|
multiple=True,
|
|
182
182
|
)
|
|
183
|
+
@click.option(
|
|
184
|
+
"--secret",
|
|
185
|
+
type=click.STRING,
|
|
186
|
+
help="Project secrets to add to the job environment",
|
|
187
|
+
multiple=True,
|
|
188
|
+
)
|
|
183
189
|
@click.option(
|
|
184
190
|
"--git-uri",
|
|
185
191
|
type=click.STRING,
|
|
@@ -222,6 +228,7 @@ def notebook(
|
|
|
222
228
|
custom_image,
|
|
223
229
|
env,
|
|
224
230
|
credential,
|
|
231
|
+
secret,
|
|
225
232
|
apt_packages,
|
|
226
233
|
pip_packages,
|
|
227
234
|
conda_packages,
|
|
@@ -254,7 +261,7 @@ def notebook(
|
|
|
254
261
|
data=dict(datasets=datasets),
|
|
255
262
|
model=dict(checkpoints=checkpoints),
|
|
256
263
|
environment=dict(
|
|
257
|
-
credentials=[k for k in credential],
|
|
264
|
+
credentials=[k for k in credential], secrets=[s for s in secret]
|
|
258
265
|
),
|
|
259
266
|
)
|
|
260
267
|
|
|
@@ -508,6 +515,12 @@ def notebook(
|
|
|
508
515
|
help="Third Party Credentials to add to the job environment",
|
|
509
516
|
multiple=True,
|
|
510
517
|
)
|
|
518
|
+
@click.option(
|
|
519
|
+
"--secret",
|
|
520
|
+
type=click.STRING,
|
|
521
|
+
help="Project secrets to add to the job environment",
|
|
522
|
+
multiple=True,
|
|
523
|
+
)
|
|
511
524
|
@click.option(
|
|
512
525
|
"--apt-packages",
|
|
513
526
|
type=click.STRING,
|
|
@@ -564,6 +577,7 @@ def training(
|
|
|
564
577
|
custom_image,
|
|
565
578
|
env,
|
|
566
579
|
credential,
|
|
580
|
+
secret,
|
|
567
581
|
apt_packages,
|
|
568
582
|
pip_packages,
|
|
569
583
|
conda_packages,
|
|
@@ -596,7 +610,7 @@ def training(
|
|
|
596
610
|
data=dict(datasets=datasets),
|
|
597
611
|
model=dict(checkpoints=checkpoints),
|
|
598
612
|
environment=dict(
|
|
599
|
-
credentials=[k for k in credential],
|
|
613
|
+
credentials=[k for k in credential], secrets=[s for s in secret]
|
|
600
614
|
),
|
|
601
615
|
)
|
|
602
616
|
|
|
@@ -852,6 +866,12 @@ def training(
|
|
|
852
866
|
help="Third Party Credentials to add to the job environment",
|
|
853
867
|
multiple=True,
|
|
854
868
|
)
|
|
869
|
+
@click.option(
|
|
870
|
+
"--secret",
|
|
871
|
+
type=click.STRING,
|
|
872
|
+
help="Project secrets to add to the job environment",
|
|
873
|
+
multiple=True,
|
|
874
|
+
)
|
|
855
875
|
@click.option(
|
|
856
876
|
"--apt-packages",
|
|
857
877
|
type=click.STRING,
|
|
@@ -908,6 +928,7 @@ def inference(
|
|
|
908
928
|
custom_image,
|
|
909
929
|
env,
|
|
910
930
|
credential,
|
|
931
|
+
secret,
|
|
911
932
|
apt_packages,
|
|
912
933
|
pip_packages,
|
|
913
934
|
conda_packages,
|
|
@@ -934,6 +955,7 @@ def inference(
|
|
|
934
955
|
model=dict(checkpoints=checkpoints),
|
|
935
956
|
environment=dict(
|
|
936
957
|
credentials=[k for k in credential],
|
|
958
|
+
secrets=[s for s in secret],
|
|
937
959
|
),
|
|
938
960
|
)
|
|
939
961
|
|
|
@@ -1171,6 +1193,12 @@ def from_json(config, attach, connect, file):
|
|
|
1171
1193
|
help="Third Party Credentials to add to the job environment",
|
|
1172
1194
|
multiple=True,
|
|
1173
1195
|
)
|
|
1196
|
+
@click.option(
|
|
1197
|
+
"--secret",
|
|
1198
|
+
type=click.STRING,
|
|
1199
|
+
help="Project secrets to add to the job environment",
|
|
1200
|
+
multiple=True,
|
|
1201
|
+
)
|
|
1174
1202
|
@click.option(
|
|
1175
1203
|
"--apt-packages",
|
|
1176
1204
|
type=click.STRING,
|
|
@@ -1231,6 +1259,7 @@ def endpoint(
|
|
|
1231
1259
|
custom_image,
|
|
1232
1260
|
env,
|
|
1233
1261
|
credential,
|
|
1262
|
+
secret,
|
|
1234
1263
|
apt_packages,
|
|
1235
1264
|
pip_packages,
|
|
1236
1265
|
conda_packages,
|
|
@@ -1258,6 +1287,7 @@ def endpoint(
|
|
|
1258
1287
|
model=dict(checkpoints=checkpoints),
|
|
1259
1288
|
environment=dict(
|
|
1260
1289
|
credentials=[k for k in credential],
|
|
1290
|
+
secrets=[s for s in secret],
|
|
1261
1291
|
),
|
|
1262
1292
|
)
|
|
1263
1293
|
|