proximl 0.5.7__py3-none-any.whl → 0.5.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. proximl/cli/project/__init__.py +153 -0
  2. proximl/cli/project/key.py +124 -0
  3. proximl/cli/project/secret.py +71 -0
  4. proximl/projects/__init__.py +3 -0
  5. proximl/projects/data_connectors.py +63 -0
  6. proximl/projects/datastores.py +58 -0
  7. proximl/projects/keys.py +71 -0
  8. proximl/projects/projects.py +83 -0
  9. proximl/projects/secrets.py +70 -0
  10. proximl/projects/services.py +63 -0
  11. {proximl-0.5.7.dist-info → proximl-0.5.9.dist-info}/METADATA +1 -1
  12. {proximl-0.5.7.dist-info → proximl-0.5.9.dist-info}/RECORD +30 -6
  13. tests/integration/projects/__init__.py +0 -0
  14. tests/integration/projects/conftest.py +8 -0
  15. tests/integration/projects/test_projects_integration.py +38 -0
  16. tests/integration/projects/test_projects_keys_integration.py +43 -0
  17. tests/integration/projects/test_projects_secrets_integration.py +44 -0
  18. tests/unit/projects/__init__.py +0 -0
  19. tests/unit/projects/test_project_data_connectors_unit.py +102 -0
  20. tests/unit/projects/test_project_datastores_unit.py +96 -0
  21. tests/unit/projects/test_project_keys_unit.py +96 -0
  22. tests/unit/projects/test_project_secrets_unit.py +101 -0
  23. tests/unit/projects/test_project_services_unit.py +102 -0
  24. tests/unit/projects/test_projects_unit.py +128 -0
  25. tests/unit/test_auth_unit.py +30 -0
  26. tests/unit/test_proximl_unit.py +54 -0
  27. {proximl-0.5.7.dist-info → proximl-0.5.9.dist-info}/LICENSE +0 -0
  28. {proximl-0.5.7.dist-info → proximl-0.5.9.dist-info}/WHEEL +0 -0
  29. {proximl-0.5.7.dist-info → proximl-0.5.9.dist-info}/entry_points.txt +0 -0
  30. {proximl-0.5.7.dist-info → proximl-0.5.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,153 @@
1
+ import click
2
+ from proximl.cli import cli, pass_config, search_by_id_name
3
+
4
+
5
+ @cli.group()
6
+ @pass_config
7
+ def project(config):
8
+ """proxiML project commands."""
9
+ pass
10
+
11
+
12
+ @project.command()
13
+ @pass_config
14
+ def list(config):
15
+ """List projects."""
16
+ data = [
17
+ ["ID", "NAME", "OWNER", "MINE"],
18
+ [
19
+ "-" * 80,
20
+ "-" * 80,
21
+ "-" * 80,
22
+ "-" * 80,
23
+ ],
24
+ ]
25
+
26
+ projects = config.proximl.run(config.proximl.client.projects.list())
27
+
28
+ for project in projects:
29
+ data.append(
30
+ [
31
+ project.id,
32
+ project.name,
33
+ project.owner_name,
34
+ "X" if project.is_owner else "",
35
+ ]
36
+ )
37
+
38
+ for row in data:
39
+ click.echo(
40
+ "{: >38.36} {: >30.28} {: >15.13} {: >4.4}" "".format(*row),
41
+ file=config.stdout,
42
+ )
43
+
44
+
45
+ @project.command()
46
+ @click.argument("name", type=click.STRING)
47
+ @pass_config
48
+ def create(config, name):
49
+ """
50
+ Create a project.
51
+
52
+ Project is created with the specified NAME.
53
+ """
54
+
55
+ return config.proximl.run(
56
+ config.proximl.client.projects.create(
57
+ name=name,
58
+ )
59
+ )
60
+
61
+
62
+ @project.command()
63
+ @click.argument("project", type=click.STRING)
64
+ @pass_config
65
+ def remove(config, project):
66
+ """
67
+ Remove a project.
68
+
69
+ PROJECT may be specified by name or ID, but ID is preferred.
70
+ """
71
+ projects = config.proximl.run(config.proximl.client.projects.list())
72
+
73
+ found = search_by_id_name(project, projects)
74
+ if None is found:
75
+ raise click.UsageError("Cannot find specified project.")
76
+
77
+ return config.proximl.run(found.remove())
78
+
79
+
80
+ @project.command()
81
+ @pass_config
82
+ def list_datastores(config):
83
+ """List project datastores."""
84
+ data = [
85
+ ["ID", "NAME", "TYPE", "REGION_UUID"],
86
+ [
87
+ "-" * 80,
88
+ "-" * 80,
89
+ "-" * 80,
90
+ "-" * 80,
91
+ ],
92
+ ]
93
+ project = config.proximl.run(
94
+ config.proximl.client.projects.get(config.proximl.client.project)
95
+ )
96
+
97
+ datastores = config.proximl.run(project.list_datastores())
98
+
99
+ for datastore in datastores:
100
+ data.append(
101
+ [
102
+ datastore.id,
103
+ datastore.name,
104
+ datastore.type,
105
+ datastore.region_uuid,
106
+ ]
107
+ )
108
+
109
+ for row in data:
110
+ click.echo(
111
+ "{: >38.36} {: >30.28} {: >15.13} {: >38.36}" "".format(*row),
112
+ file=config.stdout,
113
+ )
114
+
115
+
116
+ @project.command()
117
+ @pass_config
118
+ def list_services(config):
119
+ """List project services."""
120
+ data = [
121
+ ["ID", "NAME", "HOSTNAME", "REGION_UUID"],
122
+ [
123
+ "-" * 80,
124
+ "-" * 80,
125
+ "-" * 80,
126
+ "-" * 80,
127
+ ],
128
+ ]
129
+ project = config.proximl.run(
130
+ config.proximl.client.projects.get(config.proximl.client.project)
131
+ )
132
+
133
+ services = config.proximl.run(project.list_services())
134
+
135
+ for service in services:
136
+ data.append(
137
+ [
138
+ service.id,
139
+ service.name,
140
+ service.hostname,
141
+ service.region_uuid,
142
+ ]
143
+ )
144
+
145
+ for row in data:
146
+ click.echo(
147
+ "{: >38.36} {: >30.28} {: >30.28} {: >38.36}" "".format(*row),
148
+ file=config.stdout,
149
+ )
150
+
151
+
152
+ from proximl.cli.project.secret import secret
153
+ from proximl.cli.project.key import key
@@ -0,0 +1,124 @@
1
+ import click
2
+ import os
3
+ import json
4
+ import base64
5
+ from pathlib import Path
6
+ from proximl.cli import pass_config
7
+ from proximl.cli.project import project
8
+
9
+
10
+ @project.group()
11
+ @pass_config
12
+ def key(config):
13
+ """proxiML project key commands."""
14
+ pass
15
+
16
+
17
+ @key.command()
18
+ @pass_config
19
+ def list(config):
20
+ """List keys."""
21
+ data = [
22
+ ["TYPE", "KEY ID", "UPDATED AT"],
23
+ [
24
+ "-" * 80,
25
+ "-" * 80,
26
+ "-" * 80,
27
+ ],
28
+ ]
29
+ project = config.proximl.run(config.proximl.client.projects.get_current())
30
+ keys = config.proximl.run(project.keys.list())
31
+
32
+ for key in keys:
33
+ data.append(
34
+ [
35
+ key.type,
36
+ key.key_id,
37
+ key.updated_at.isoformat(timespec="seconds"),
38
+ ]
39
+ )
40
+
41
+ for row in data:
42
+ click.echo(
43
+ "{: >13.11} {: >37.35} {: >28.26}" "".format(*row),
44
+ file=config.stdout,
45
+ )
46
+
47
+
48
+ @key.command()
49
+ @click.argument(
50
+ "type",
51
+ type=click.Choice(
52
+ [
53
+ "aws",
54
+ "azure",
55
+ "docker",
56
+ "gcp",
57
+ "huggingface",
58
+ "kaggle",
59
+ "ngc",
60
+ "wasabi",
61
+ ],
62
+ case_sensitive=False,
63
+ ),
64
+ )
65
+ @pass_config
66
+ def put(config, type):
67
+ """
68
+ Set a key.
69
+
70
+ A key is uploaded.
71
+ """
72
+ project = config.proximl.run(config.proximl.client.projects.get_current())
73
+
74
+ tenant = None
75
+
76
+ if type in ["aws", "wasabi"]:
77
+ key_id = click.prompt("Enter the key ID", type=str, hide_input=False)
78
+ secret = click.prompt("Enter the secret key", type=str, hide_input=True)
79
+ elif type == "azure":
80
+ key_id = click.prompt(
81
+ "Enter the Application (client) ID", type=str, hide_input=False
82
+ )
83
+ tenant = click.prompt(
84
+ "Enter the Directory (tenant) ley", type=str, hide_input=False
85
+ )
86
+ secret = click.prompt("Enter the client secret", type=str, hide_input=True)
87
+ elif type in ["docker", "huggingface"]:
88
+ key_id = click.prompt("Enter the username", type=str, hide_input=False)
89
+ secret = click.prompt("Enter the access token", type=str, hide_input=True)
90
+ elif type in ["gcp", "kaggle"]:
91
+ file_name = click.prompt(
92
+ "Enter the path of the credentials file",
93
+ type=click.Path(
94
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True
95
+ ),
96
+ hide_input=False,
97
+ )
98
+ key_id = os.path.basename(file_name)
99
+ with open(file_name) as f:
100
+ secret = json.load(f)
101
+ secret = json.dumps(secret)
102
+ elif type == "ngc":
103
+ key_id = "$oauthtoken"
104
+ secret = click.prompt("Enter the access token", type=str, hide_input=True)
105
+ else:
106
+ raise click.UsageError("Unsupported key type")
107
+
108
+ return config.proximl.run(
109
+ project.keys.put(type=type, key_id=key_id, secret=secret, tenant=tenant)
110
+ )
111
+
112
+
113
+ @key.command()
114
+ @click.argument("name", type=click.STRING)
115
+ @pass_config
116
+ def remove(config, name):
117
+ """
118
+ Remove a key.
119
+
120
+
121
+ """
122
+ project = config.proximl.run(config.proximl.client.projects.get_current())
123
+
124
+ return config.proximl.run(project.key.remove(name))
@@ -0,0 +1,71 @@
1
+ import click
2
+ from proximl.cli import pass_config
3
+ from proximl.cli.project import project
4
+
5
+
6
+ @project.group()
7
+ @pass_config
8
+ def secret(config):
9
+ """proxiML project secret commands."""
10
+ pass
11
+
12
+
13
+ @secret.command()
14
+ @pass_config
15
+ def list(config):
16
+ """List secrets."""
17
+ data = [
18
+ ["NAME", "CREATED BY", "UPDATED AT"],
19
+ [
20
+ "-" * 80,
21
+ "-" * 80,
22
+ "-" * 80,
23
+ ],
24
+ ]
25
+ project = config.proximl.run(config.proximl.client.projects.get_current())
26
+ secrets = config.proximl.run(project.secrets.list())
27
+
28
+ for secret in secrets:
29
+ data.append(
30
+ [
31
+ secret.name,
32
+ secret.created_by,
33
+ secret.updated_at.isoformat(timespec="seconds"),
34
+ ]
35
+ )
36
+
37
+ for row in data:
38
+ click.echo(
39
+ "{: >38.36} {: >30.28} {: >28.26}" "".format(*row),
40
+ file=config.stdout,
41
+ )
42
+
43
+
44
+ @secret.command()
45
+ @click.argument("name", type=click.STRING)
46
+ @pass_config
47
+ def put(config, name):
48
+ """
49
+ Set a secret value.
50
+
51
+ Secret is created with the specified NAME.
52
+ """
53
+ project = config.proximl.run(config.proximl.client.projects.get_current())
54
+
55
+ value = click.prompt("Enter the secret value", type=str, hide_input=True)
56
+
57
+ return config.proximl.run(project.secrets.put(name=name, value=value))
58
+
59
+
60
+ @secret.command()
61
+ @click.argument("name", type=click.STRING)
62
+ @pass_config
63
+ def remove(config, name):
64
+ """
65
+ Remove a secret.
66
+
67
+
68
+ """
69
+ project = config.proximl.run(config.proximl.client.projects.get_current())
70
+
71
+ return config.proximl.run(project.secret.remove(name))
@@ -0,0 +1,3 @@
1
+ from .projects import Projects, Project
2
+
3
+ __all__ = ["Projects", "Project"]
@@ -0,0 +1,63 @@
1
+ import json
2
+ import logging
3
+
4
+
5
+ class ProjectDataConnectors(object):
6
+ def __init__(self, proximl, project_id):
7
+ self.proximl = proximl
8
+ self.project_id = project_id
9
+
10
+ async def list(self, **kwargs):
11
+ resp = await self.proximl._query(
12
+ f"/project/{self.project_id}/data_connectors", "GET", kwargs
13
+ )
14
+ data_connectors = [
15
+ ProjectDataConnector(self.proximl, **data_connector)
16
+ for data_connector in resp
17
+ ]
18
+ return data_connectors
19
+
20
+ async def refresh(self):
21
+ await self.proximl._query(
22
+ f"/project/{self.project_id}/data_connectors", "PATCH"
23
+ )
24
+
25
+
26
+ class ProjectDataConnector:
27
+ def __init__(self, proximl, **kwargs):
28
+ self.proximl = proximl
29
+ self._entity = kwargs
30
+ self._id = self._entity.get("id")
31
+ self._project_uuid = self._entity.get("project_uuid")
32
+ self._name = self._entity.get("name")
33
+ self._type = self._entity.get("type")
34
+ self._region_uuid = self._entity.get("region_uuid")
35
+
36
+ @property
37
+ def id(self) -> str:
38
+ return self._id
39
+
40
+ @property
41
+ def project_uuid(self) -> str:
42
+ return self._project_uuid
43
+
44
+ @property
45
+ def name(self) -> str:
46
+ return self._name
47
+
48
+ @property
49
+ def type(self) -> str:
50
+ return self._type
51
+
52
+ @property
53
+ def region_uuid(self) -> str:
54
+ return self._region_uuid
55
+
56
+ def __str__(self):
57
+ return json.dumps({k: v for k, v in self._entity.items()})
58
+
59
+ def __repr__(self):
60
+ return f"ProjectDataConnector( proximl , **{self._entity.__repr__()})"
61
+
62
+ def __bool__(self):
63
+ return bool(self._id)
@@ -0,0 +1,58 @@
1
+ import json
2
+ import logging
3
+
4
+
5
+ class ProjectDatastores(object):
6
+ def __init__(self, proximl, project_id):
7
+ self.proximl = proximl
8
+ self.project_id = project_id
9
+
10
+ async def list(self, **kwargs):
11
+ resp = await self.proximl._query(
12
+ f"/project/{self.project_id}/datastores", "GET", kwargs
13
+ )
14
+ datastores = [ProjectDatastore(self.proximl, **datastore) for datastore in resp]
15
+ return datastores
16
+
17
+ async def refresh(self):
18
+ await self.proximl._query(f"/project/{self.project_id}/datastores", "PATCH")
19
+
20
+
21
+ class ProjectDatastore:
22
+ def __init__(self, proximl, **kwargs):
23
+ self.proximl = proximl
24
+ self._entity = kwargs
25
+ self._id = self._entity.get("id")
26
+ self._project_uuid = self._entity.get("project_uuid")
27
+ self._name = self._entity.get("name")
28
+ self._type = self._entity.get("type")
29
+ self._region_uuid = self._entity.get("region_uuid")
30
+
31
+ @property
32
+ def id(self) -> str:
33
+ return self._id
34
+
35
+ @property
36
+ def project_uuid(self) -> str:
37
+ return self._project_uuid
38
+
39
+ @property
40
+ def name(self) -> str:
41
+ return self._name
42
+
43
+ @property
44
+ def type(self) -> str:
45
+ return self._type
46
+
47
+ @property
48
+ def region_uuid(self) -> str:
49
+ return self._region_uuid
50
+
51
+ def __str__(self):
52
+ return json.dumps({k: v for k, v in self._entity.items()})
53
+
54
+ def __repr__(self):
55
+ return f"ProjectDatastore( proximl , **{self._entity.__repr__()})"
56
+
57
+ def __bool__(self):
58
+ return bool(self._id)
@@ -0,0 +1,71 @@
1
+ import json
2
+ import logging
3
+ from datetime import datetime
4
+ from dateutil import parser, tz
5
+
6
+
7
+ class ProjectKeys(object):
8
+ def __init__(self, proximl, project_id):
9
+ self.proximl = proximl
10
+ self.project_id = project_id
11
+
12
+ async def list(self, **kwargs):
13
+ resp = await self.proximl._query(
14
+ f"/project/{self.project_id}/keys", "GET", kwargs
15
+ )
16
+ keys = [ProjectKey(self.proximl, **service) for service in resp]
17
+ return keys
18
+
19
+ async def put(self, type, key_id, secret, tenant=None, **kwargs):
20
+ data = dict(key_id=key_id, secret=secret, tenant=tenant)
21
+ payload = {k: v for k, v in data.items() if v is not None}
22
+ logging.info(f"Creating Project Key {type}")
23
+ resp = await self.proximl._query(
24
+ f"/project/{self.project_id}/key/{type}", "PUT", None, payload
25
+ )
26
+ key = ProjectKey(self.proximl, **resp)
27
+ logging.info(f"Created Project Key {type} in project {self.project_id}")
28
+
29
+ return key
30
+
31
+ async def remove(self, type, **kwargs):
32
+ await self.proximl._query(
33
+ f"/project/{self.project_id}/key/{type}", "DELETE", kwargs
34
+ )
35
+
36
+
37
+ class ProjectKey:
38
+ def __init__(self, proximl, **kwargs):
39
+ self.proximl = proximl
40
+ self._entity = kwargs
41
+ self._type = self._entity.get("type")
42
+ self._project_uuid = self._entity.get("project_uuid")
43
+ self._key_id = self._entity.get("key_id")
44
+ self._updated_at = self._entity.get("updatedAt")
45
+
46
+ @property
47
+ def type(self) -> str:
48
+ return self._type
49
+
50
+ @property
51
+ def project_uuid(self) -> str:
52
+ return self._project_uuid
53
+
54
+ @property
55
+ def key_id(self) -> str:
56
+ return self._key_id
57
+
58
+ @property
59
+ def updated_at(self) -> datetime:
60
+ timestamp = parser.isoparse(self._updated_at)
61
+ timezone = tz.tzlocal()
62
+ return timestamp.astimezone(timezone)
63
+
64
+ def __str__(self):
65
+ return json.dumps({k: v for k, v in self._entity.items()})
66
+
67
+ def __repr__(self):
68
+ return f"ProjectKey( proximl , **{self._entity.__repr__()})"
69
+
70
+ def __bool__(self):
71
+ return bool(self._type)
@@ -0,0 +1,83 @@
1
+ import json
2
+ import logging
3
+ from .datastores import ProjectDatastores
4
+ from .data_connectors import ProjectDataConnectors
5
+ from .services import ProjectServices
6
+ from .keys import ProjectKeys
7
+ from .secrets import ProjectSecrets
8
+
9
+
10
+ class Projects(object):
11
+ def __init__(self, proximl):
12
+ self.proximl = proximl
13
+
14
+ async def get(self, id, **kwargs):
15
+ resp = await self.proximl._query(f"/project/{id}", "GET", kwargs)
16
+ return Project(self.proximl, **resp)
17
+
18
+ async def get_current(self, **kwargs):
19
+ resp = await self.proximl._query(
20
+ f"/project/{self.proximl.project}", "GET", kwargs
21
+ )
22
+ return Project(self.proximl, **resp)
23
+
24
+ async def list(self, **kwargs):
25
+ resp = await self.proximl._query(f"/project", "GET", kwargs)
26
+ projects = [Project(self.proximl, **project) for project in resp]
27
+ return projects
28
+
29
+ async def create(self, name, copy_keys=False, copy_secrets=False, **kwargs):
30
+ data = dict(name=name, copy_keys=copy_keys, copy_secrets=copy_secrets)
31
+ payload = {k: v for k, v in data.items() if v is not None}
32
+ logging.info(f"Creating Project {name}")
33
+ resp = await self.proximl._query("/project", "POST", None, payload)
34
+ project = Project(self.proximl, **resp)
35
+ logging.info(f"Created Project {name} with id {project.id}")
36
+
37
+ return project
38
+
39
+ async def remove(self, id, **kwargs):
40
+ await self.proximl._query(f"/project/{id}", "DELETE", kwargs)
41
+
42
+
43
+ class Project:
44
+ def __init__(self, proximl, **kwargs):
45
+ self.proximl = proximl
46
+ self._entity = kwargs
47
+ self._id = self._entity.get("id")
48
+ self._name = self._entity.get("name")
49
+ self._is_owner = self._entity.get("owner")
50
+ self._owner_name = self._entity.get("owner_name")
51
+ self.datastores = ProjectDatastores(self.proximl, self._id)
52
+ self.data_connectors = ProjectDataConnectors(self.proximl, self._id)
53
+ self.services = ProjectServices(self.proximl, self._id)
54
+ self.keys = ProjectKeys(self.proximl, self._id)
55
+ self.secrets = ProjectSecrets(self.proximl, self._id)
56
+
57
+ @property
58
+ def id(self) -> str:
59
+ return self._id
60
+
61
+ @property
62
+ def name(self) -> str:
63
+ return self._name
64
+
65
+ @property
66
+ def is_owner(self) -> bool:
67
+ return self._is_owner
68
+
69
+ @property
70
+ def owner_name(self) -> str:
71
+ return self._owner_name
72
+
73
+ def __str__(self):
74
+ return json.dumps({k: v for k, v in self._entity.items()})
75
+
76
+ def __repr__(self):
77
+ return f"Project( proximl , **{self._entity.__repr__()})"
78
+
79
+ def __bool__(self):
80
+ return bool(self._id)
81
+
82
+ async def remove(self):
83
+ await self.proximl._query(f"/project/{self._id}", "DELETE")
@@ -0,0 +1,70 @@
1
+ import json
2
+ import logging
3
+ from datetime import datetime
4
+ from dateutil import parser, tz
5
+
6
+
7
+ class ProjectSecrets(object):
8
+ def __init__(self, proximl, project_id):
9
+ self.proximl = proximl
10
+ self.project_id = project_id
11
+
12
+ async def list(self, **kwargs):
13
+ resp = await self.proximl._query(
14
+ f"/project/{self.project_id}/secrets", "GET", kwargs
15
+ )
16
+ secrets = [ProjectSecret(self.proximl, **service) for service in resp]
17
+ return secrets
18
+
19
+ async def put(self, name, value, **kwargs):
20
+ data = dict(value=value)
21
+ payload = {k: v for k, v in data.items() if v is not None}
22
+ logging.info(f"Creating Project Secret {name}")
23
+ resp = await self.proximl._query(
24
+ f"/project/{self.project_id}/secret/{name}", "PUT", None, payload
25
+ )
26
+ secret = ProjectSecret(self.proximl, **resp)
27
+ logging.info(f"Created Project Key {name} in project {self.project_id}")
28
+ return secret
29
+
30
+ async def remove(self, name, **kwargs):
31
+ await self.proximl._query(
32
+ f"/project/{self.project_id}/secret/{name}", "DELETE", kwargs
33
+ )
34
+
35
+
36
+ class ProjectSecret:
37
+ def __init__(self, proximl, **kwargs):
38
+ self.proximl = proximl
39
+ self._entity = kwargs
40
+ self._name = self._entity.get("name")
41
+ self._project_uuid = self._entity.get("project_uuid")
42
+ self._created_by = self._entity.get("created_by")
43
+ self._updated_at = self._entity.get("updatedAt")
44
+
45
+ @property
46
+ def name(self) -> str:
47
+ return self._name
48
+
49
+ @property
50
+ def project_uuid(self) -> str:
51
+ return self._project_uuid
52
+
53
+ @property
54
+ def created_by(self) -> str:
55
+ return self._created_by
56
+
57
+ @property
58
+ def updated_at(self) -> datetime:
59
+ timestamp = parser.isoparse(self._updated_at)
60
+ timezone = tz.tzlocal()
61
+ return timestamp.astimezone(timezone)
62
+
63
+ def __str__(self):
64
+ return json.dumps({k: v for k, v in self._entity.items()})
65
+
66
+ def __repr__(self):
67
+ return f"ProjectSecret( proximl , **{self._entity.__repr__()})"
68
+
69
+ def __bool__(self):
70
+ return bool(self._name)