remotivelabs-cli 0.0.25__py3-none-any.whl → 0.0.26__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.
- cli/broker/brokers.py +2 -2
- cli/broker/export.py +5 -5
- cli/broker/files.py +5 -5
- cli/broker/lib/broker.py +59 -49
- cli/broker/license_flows.py +11 -9
- cli/broker/licenses.py +2 -2
- cli/broker/playback.py +14 -34
- cli/broker/record.py +3 -3
- cli/broker/scripting.py +4 -4
- cli/broker/signals.py +11 -11
- cli/cloud/__init__.py +0 -1
- cli/cloud/auth.py +40 -35
- cli/cloud/auth_tokens.py +39 -36
- cli/cloud/brokers.py +24 -33
- cli/cloud/cloud_cli.py +9 -6
- cli/cloud/configs.py +19 -11
- cli/cloud/filestorage.py +63 -51
- cli/cloud/projects.py +10 -7
- cli/cloud/recordings.py +127 -108
- cli/cloud/recordings_playback.py +52 -39
- cli/cloud/rest_helper.py +247 -196
- cli/cloud/resumable_upload.py +9 -8
- cli/cloud/sample_recordings.py +5 -5
- cli/cloud/service_account_tokens.py +18 -16
- cli/cloud/service_accounts.py +9 -9
- cli/connect/__init__.py +0 -1
- cli/connect/connect.py +7 -6
- cli/connect/protopie/protopie.py +32 -16
- cli/errors.py +6 -5
- cli/remotive.py +13 -9
- cli/requirements.txt +4 -1
- cli/settings.py +9 -9
- cli/tools/__init__.py +0 -1
- cli/tools/can/__init__.py +0 -1
- cli/tools/can/can.py +8 -8
- cli/tools/tools.py +2 -2
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/METADATA +5 -3
- remotivelabs_cli-0.0.26.dist-info/RECORD +44 -0
- remotivelabs_cli-0.0.25.dist-info/RECORD +0 -44
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/entry_points.txt +0 -0
cli/cloud/configs.py
CHANGED
@@ -7,22 +7,22 @@ import requests
|
|
7
7
|
import typer
|
8
8
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
9
9
|
|
10
|
-
from . import
|
10
|
+
from .rest_helper import RestHelper as Rest
|
11
11
|
|
12
12
|
app = typer.Typer()
|
13
13
|
|
14
14
|
|
15
15
|
@app.command("list")
|
16
|
-
def list_signal_databases(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")):
|
17
|
-
|
16
|
+
def list_signal_databases(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
|
17
|
+
Rest.handle_get(f"/api/project/{project}/files/config")
|
18
18
|
|
19
19
|
|
20
20
|
@app.command("delete")
|
21
21
|
def delete(
|
22
22
|
signal_db_file: str = typer.Argument("", help="Signal database file"),
|
23
23
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
24
|
-
):
|
25
|
-
|
24
|
+
) -> None:
|
25
|
+
Rest.handle_delete(f"/api/project/{project}/files/config/{signal_db_file}")
|
26
26
|
|
27
27
|
|
28
28
|
@app.command()
|
@@ -38,35 +38,42 @@ def upload(
|
|
38
38
|
help="Path to signal database file to upload",
|
39
39
|
),
|
40
40
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
41
|
-
):
|
41
|
+
) -> None:
|
42
42
|
"""
|
43
43
|
Uploads signal database to project
|
44
44
|
"""
|
45
|
-
|
45
|
+
res = Rest.handle_put(url=f"/api/project/{project}/files/config/{os.path.basename(path)}/uploadfile", return_response=True)
|
46
|
+
if res is not None:
|
47
|
+
Rest.upload_file_with_signed_url(
|
48
|
+
path=path, url=res.text, upload_headers={"Content-Type": "application/octet-stream"}, progress_label=f"Uploading {path}..."
|
49
|
+
)
|
46
50
|
|
47
51
|
|
48
52
|
@app.command()
|
49
53
|
def download(
|
50
54
|
signal_db_file: str = typer.Argument("", help="Signal database file"),
|
51
55
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
52
|
-
):
|
56
|
+
) -> None:
|
53
57
|
with Progress(
|
54
58
|
SpinnerColumn(),
|
55
59
|
TextColumn("[progress.description]{task.description}"),
|
56
60
|
transient=True,
|
57
61
|
) as progress:
|
58
|
-
|
62
|
+
Rest.ensure_auth_token()
|
59
63
|
|
60
64
|
progress.add_task(description=f"Downloading {signal_db_file}", total=None)
|
61
65
|
|
62
66
|
# First request the download url from cloud. This is a public signed url that is valid
|
63
67
|
# for a short period of time
|
64
68
|
get_signed_url_resp = requests.get(
|
65
|
-
f"{
|
69
|
+
f"{Rest.get_base_url()}/api/project/{project}/files/config/{signal_db_file}/download",
|
70
|
+
headers=Rest.get_headers(),
|
71
|
+
allow_redirects=True,
|
72
|
+
timeout=60,
|
66
73
|
)
|
67
74
|
if get_signed_url_resp.status_code == 200:
|
68
75
|
# Next download the actual file
|
69
|
-
download_resp = requests.get(url=get_signed_url_resp.text, stream=True)
|
76
|
+
download_resp = requests.get(url=get_signed_url_resp.text, stream=True, timeout=60)
|
70
77
|
if download_resp.status_code == 200:
|
71
78
|
with open(signal_db_file, "wb") as out_file:
|
72
79
|
shutil.copyfileobj(download_resp.raw, out_file)
|
@@ -86,4 +93,5 @@ def download(
|
|
86
93
|
# print(r.status_code)
|
87
94
|
# print(r.text)
|
88
95
|
|
96
|
+
# pylint: disable=C0301
|
89
97
|
# curl -X PUT -H 'Content-Type: application/octet-stream' --upload-file docker-compose.yml 'https://storage.googleapis.com/beamylabs-fileuploads-dev/projects/beamyhack/recording/myrecording?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=recordings-upload-account%40beamycloud-dev.iam.gserviceaccount.com%2F20220729%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20220729T134012Z&X-Goog-Expires=3000&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=d1fa7639349d6453aebfce8814d6e5685af03952d07aa4e3cb0d44dba7cf5e572f684c8120dba17cbc7ea6a0ef5450542a3c745c65e04272b34265d0ddcf1b67e6f2b5bfa446264a62d77bd7faabf45ad6bd2aec5225f57004b0a31cfe0480cba063a3807d86346b1da99ecbae3f3e6da8f44f06396dfc1fdc6f89e475abdf969142cef6f369f03aff41000c8abb28aa82185246746fd6c16b6b381baa2d586382a3d3067b6376ddba2b55b2b6f9d942913a1cbfbc61491ba6a615d7d5a0d9a476c357431143e9cea1411dfad9f01b1e1176dc8c056cbf08cccfd401a55d63c19d038f3ab42b712abc48d759047ac07862c4fae937c341e19b568bb60a4e4086'
|
cli/cloud/filestorage.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
import os.path
|
2
|
+
import sys
|
2
3
|
from pathlib import Path
|
3
4
|
|
4
5
|
import typer
|
5
6
|
|
6
7
|
from ..errors import ErrorPrinter
|
7
|
-
from . import rest_helper as rest
|
8
8
|
from . import resumable_upload as upload
|
9
|
+
from .rest_helper import RestHelper as Rest
|
9
10
|
|
10
11
|
app = typer.Typer(
|
11
12
|
rich_markup_mode="rich",
|
@@ -22,7 +23,7 @@ Copy file from local to remote storage and vice versa, list and delete files.
|
|
22
23
|
def list_files(
|
23
24
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
24
25
|
prefix: str = typer.Argument(default="rcs://", help="Remote storage path"),
|
25
|
-
):
|
26
|
+
) -> None:
|
26
27
|
"""
|
27
28
|
Listing remote files
|
28
29
|
|
@@ -40,9 +41,9 @@ def list_files(
|
|
40
41
|
prefix = __check_rcs_path(prefix)
|
41
42
|
else:
|
42
43
|
ErrorPrinter.print_hint("Path must start with rcs://")
|
43
|
-
exit(1)
|
44
|
+
sys.exit(1)
|
44
45
|
|
45
|
-
|
46
|
+
Rest.handle_get(
|
46
47
|
f"/api/project/{project}/files/storage{prefix}",
|
47
48
|
)
|
48
49
|
|
@@ -51,7 +52,7 @@ def list_files(
|
|
51
52
|
def delete_file(
|
52
53
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
53
54
|
path: str = typer.Argument(default=..., help="Remote storage path to file to delete"),
|
54
|
-
):
|
55
|
+
) -> None:
|
55
56
|
"""
|
56
57
|
[red]Deletes[/red] a file from remote storage, this cannot be undone :fire:
|
57
58
|
|
@@ -61,19 +62,19 @@ def delete_file(
|
|
61
62
|
prefix = __check_rcs_path(path)
|
62
63
|
else:
|
63
64
|
ErrorPrinter.print_hint("Path must start with rcs://")
|
64
|
-
exit(1)
|
65
|
+
sys.exit(1)
|
65
66
|
|
66
|
-
|
67
|
+
Rest.handle_delete(
|
67
68
|
f"/api/project/{project}/files/storage{prefix}",
|
68
69
|
)
|
69
70
|
|
70
71
|
|
71
72
|
@app.command(name="cp")
|
72
|
-
def copy_file(
|
73
|
+
def copy_file( # noqa: C901 # type: ignore[too-many-branches] # pylint: disable=no-member
|
73
74
|
source: str = typer.Argument(default=..., help="Remote or local path to source file"),
|
74
75
|
dest: str = typer.Argument(default=..., help="Remote or local path to destination file"),
|
75
76
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
76
|
-
):
|
77
|
+
) -> None:
|
77
78
|
"""
|
78
79
|
Copies a file to or from remote storage
|
79
80
|
|
@@ -86,58 +87,69 @@ def copy_file(
|
|
86
87
|
|
87
88
|
if not source.startswith("rcs://") and not dest.startswith("rcs://"):
|
88
89
|
ErrorPrinter.print_hint("Source or destination path must be an rcs:// path")
|
89
|
-
exit(2)
|
90
|
+
sys.exit(2)
|
90
91
|
|
91
92
|
if source.startswith("rcs://") and dest.startswith("rcs://"):
|
92
93
|
ErrorPrinter.print_hint("Currently one of source and destination path must be a local path")
|
93
|
-
exit(2)
|
94
|
+
sys.exit(2)
|
94
95
|
|
95
96
|
if source.startswith("rcs://"):
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
97
|
+
__copy_to_local(source=source, dest=dest, project=project)
|
98
|
+
else:
|
99
|
+
__copy_to_remote(source=source, dest=dest, project=project)
|
100
|
+
|
101
|
+
|
102
|
+
def __copy_to_remote(source: str, dest: str, project: str) -> None:
|
103
|
+
path = Path(source)
|
104
|
+
if not path.exists():
|
105
|
+
ErrorPrinter.print_hint("Source file does not exist")
|
106
|
+
sys.exit(1)
|
107
|
+
filename = source.rsplit("/", 1)[-1]
|
108
|
+
rcs_path = __check_rcs_path(dest)
|
109
|
+
if rcs_path.endswith("/"):
|
110
|
+
rcs_path = rcs_path + filename
|
111
|
+
res = Rest.handle_post(f"/api/project/{project}/files/storage{rcs_path}", return_response=True)
|
112
|
+
if res is None:
|
113
|
+
return
|
114
|
+
json = res.json()
|
115
|
+
url = json["url"]
|
116
|
+
content_type = json["contentType"]
|
117
|
+
try:
|
118
|
+
upload.upload_signed_url(url, source, content_type)
|
119
|
+
except IsADirectoryError:
|
120
|
+
ErrorPrinter.print_hint(f"Supplied source file '{source}' is a directory but must be a file")
|
121
|
+
|
122
|
+
|
123
|
+
def __copy_to_local(source: str, dest: str, project: str) -> None:
|
124
|
+
rcs_path = __check_rcs_path(source)
|
125
|
+
filename = source.rsplit("/", 1)[-1]
|
126
|
+
path = Path(dest)
|
127
|
+
if path.is_dir():
|
128
|
+
if not path.exists():
|
129
|
+
ErrorPrinter.print_generic_error("Destination directory does not exist")
|
130
|
+
sys.exit(1)
|
106
131
|
else:
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
132
|
+
dest = os.path.join(path.absolute(), filename)
|
133
|
+
|
134
|
+
else:
|
135
|
+
if not path.parent.is_dir() or not path.parent.exists():
|
136
|
+
ErrorPrinter.print_generic_error("Destination directory does not exist")
|
137
|
+
sys.exit(1)
|
138
|
+
dest = str(Path(dest).absolute())
|
139
|
+
|
140
|
+
res = Rest.handle_get(
|
141
|
+
f"/api/project/{project}/files/storage{rcs_path}?download=true",
|
142
|
+
return_response=True,
|
143
|
+
)
|
144
|
+
if res is None:
|
145
|
+
return
|
111
146
|
|
112
|
-
|
113
|
-
f"/api/project/{project}/files/storage{rcs_path}?download=true",
|
114
|
-
return_response=True,
|
115
|
-
)
|
147
|
+
Rest.download_file(save_file_name=dest, url=res.text)
|
116
148
|
|
117
|
-
rest.download_file(save_file_name=dest, url=res.text)
|
118
149
|
|
119
|
-
|
120
|
-
path = Path(source)
|
121
|
-
if not path.exists():
|
122
|
-
ErrorPrinter.print_hint("Source file does not exist")
|
123
|
-
exit(1)
|
124
|
-
filename = source.rsplit("/", 1)[-1]
|
125
|
-
rcs_path = __check_rcs_path(dest)
|
126
|
-
if rcs_path.endswith("/"):
|
127
|
-
rcs_path = rcs_path + filename
|
128
|
-
res = rest.handle_post(f"/api/project/{project}/files/storage{rcs_path}", return_response=True)
|
129
|
-
json = res.json()
|
130
|
-
url = json["url"]
|
131
|
-
content_type = json["contentType"]
|
132
|
-
try:
|
133
|
-
upload.upload_signed_url(url, source, content_type)
|
134
|
-
except IsADirectoryError:
|
135
|
-
ErrorPrinter.print_hint(f"Supplied source file '{source}' is a directory but must be a file")
|
136
|
-
|
137
|
-
|
138
|
-
def __check_rcs_path(path: str):
|
150
|
+
def __check_rcs_path(path: str) -> str:
|
139
151
|
rcs_path = path.replace("rcs://", "/")
|
140
152
|
if rcs_path.startswith("/."):
|
141
153
|
ErrorPrinter.print_hint("Invalid path")
|
142
|
-
exit(1)
|
154
|
+
sys.exit(1)
|
143
155
|
return rcs_path
|
cli/cloud/projects.py
CHANGED
@@ -2,14 +2,17 @@ import json
|
|
2
2
|
|
3
3
|
import typer
|
4
4
|
|
5
|
-
from . import
|
5
|
+
from .rest_helper import RestHelper as Rest
|
6
6
|
|
7
7
|
app = typer.Typer()
|
8
8
|
|
9
9
|
|
10
10
|
@app.command(name="list", help="List your projects")
|
11
|
-
def list_projects(organisation: str = typer.Option(..., help="Organisation ID", envvar="REMOTIVE_CLOUD_ORGANISATION")):
|
12
|
-
r =
|
11
|
+
def list_projects(organisation: str = typer.Option(..., help="Organisation ID", envvar="REMOTIVE_CLOUD_ORGANISATION")) -> None:
|
12
|
+
r = Rest.handle_get(url=f"/api/bu/{organisation}/me", return_response=True)
|
13
|
+
if r is None:
|
14
|
+
return
|
15
|
+
|
13
16
|
if r.status_code == 200:
|
14
17
|
# extract the project uid parts
|
15
18
|
projects = r.json()["projects"]
|
@@ -25,16 +28,16 @@ def create_project(
|
|
25
28
|
organisation: str = typer.Option(..., help="Organisation ID", envvar="REMOTIVE_CLOUD_ORGANISATION"),
|
26
29
|
project_uid: str = typer.Option(..., help="Project UID"),
|
27
30
|
project_display_name: str = typer.Option(default="", help="Project display name"),
|
28
|
-
):
|
31
|
+
) -> None:
|
29
32
|
create_project_req = {
|
30
33
|
"uid": project_uid,
|
31
34
|
"displayName": project_display_name if project_display_name != "" else project_uid,
|
32
35
|
"description": "",
|
33
36
|
}
|
34
37
|
|
35
|
-
|
38
|
+
Rest.handle_post(url=f"/api/bu/{organisation}/project", body=json.dumps(create_project_req))
|
36
39
|
|
37
40
|
|
38
41
|
@app.command(name="delete")
|
39
|
-
def delete(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")):
|
40
|
-
|
42
|
+
def delete(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
|
43
|
+
Rest.handle_delete(url=f"/api/project/{project}")
|