remotivelabs-cli 0.3.0__py3-none-any.whl → 0.3.2__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.
Potentially problematic release.
This version of remotivelabs-cli might be problematic. Click here for more details.
- cli/broker/__init__.py +36 -0
- cli/broker/discovery.py +43 -0
- cli/broker/export.py +6 -36
- cli/broker/files.py +12 -12
- cli/broker/lib/broker.py +13 -18
- cli/broker/lib/helper.py +6 -7
- cli/broker/license_flows.py +11 -13
- cli/broker/playback.py +10 -10
- cli/broker/record.py +4 -4
- cli/broker/scripting.py +6 -9
- cli/broker/signals.py +17 -19
- cli/cloud/auth/cmd.py +17 -14
- cli/cloud/auth/login.py +16 -26
- cli/cloud/auth_tokens.py +9 -13
- cli/cloud/brokers.py +5 -9
- cli/cloud/configs.py +4 -17
- cli/cloud/organisations.py +8 -10
- cli/cloud/projects.py +3 -3
- cli/cloud/recordings.py +35 -61
- cli/cloud/recordings_playback.py +22 -22
- cli/cloud/resumable_upload.py +6 -6
- cli/cloud/service_account_tokens.py +3 -2
- cli/cloud/storage/cmd.py +2 -3
- cli/cloud/storage/copy.py +2 -1
- cli/connect/connect.py +4 -4
- cli/connect/protopie/protopie.py +21 -29
- cli/remotive.py +4 -7
- cli/settings/core.py +4 -2
- cli/settings/migration/migration_tools.py +2 -1
- cli/tools/can/can.py +4 -7
- cli/topology/__init__.py +3 -0
- cli/topology/cmd.py +61 -83
- cli/topology/start_trial.py +105 -0
- cli/typer/typer_utils.py +3 -6
- cli/utils/console.py +61 -0
- cli/utils/rest_helper.py +23 -16
- cli/utils/versions.py +2 -4
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/METADATA +1 -1
- remotivelabs_cli-0.3.2.dist-info/RECORD +74 -0
- cli/broker/brokers.py +0 -93
- cli/errors.py +0 -44
- remotivelabs_cli-0.3.0.dist-info/RECORD +0 -71
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/entry_points.txt +0 -0
cli/cloud/recordings.py
CHANGED
|
@@ -17,10 +17,17 @@ from rich.progress import Progress, SpinnerColumn, TaskID, TextColumn, track
|
|
|
17
17
|
from typing_extensions import Annotated
|
|
18
18
|
|
|
19
19
|
from cli.cloud.uri import URI
|
|
20
|
-
from cli.errors import ErrorPrinter
|
|
21
20
|
from cli.typer import typer_utils
|
|
21
|
+
from cli.utils.console import (
|
|
22
|
+
print_generic_error,
|
|
23
|
+
print_generic_message,
|
|
24
|
+
print_grpc_error,
|
|
25
|
+
print_hint,
|
|
26
|
+
print_success,
|
|
27
|
+
print_unformatted,
|
|
28
|
+
print_unformatted_to_stderr,
|
|
29
|
+
)
|
|
22
30
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
23
|
-
from cli.utils.rest_helper import err_console
|
|
24
31
|
|
|
25
32
|
from ..broker.lib.broker import Broker
|
|
26
33
|
from .recordings_playback import app as playback_app
|
|
@@ -30,7 +37,8 @@ app.add_typer(playback_app, name="playback")
|
|
|
30
37
|
|
|
31
38
|
|
|
32
39
|
def uid(p: Any) -> Any:
|
|
33
|
-
print
|
|
40
|
+
# TODO: use log instead of print for debug information?
|
|
41
|
+
print_generic_message(p)
|
|
34
42
|
return p["uid"]
|
|
35
43
|
|
|
36
44
|
|
|
@@ -38,19 +46,6 @@ def uid(p: Any) -> Any:
|
|
|
38
46
|
# ruff: noqa: C901
|
|
39
47
|
|
|
40
48
|
|
|
41
|
-
# to be used in options
|
|
42
|
-
# autocompletion=project_names)
|
|
43
|
-
def project_names() -> Any:
|
|
44
|
-
r = requests.get(f"{Rest.get_base_url()}/api/bu/{Rest.get_org()}/project", headers=Rest.get_headers(), timeout=60)
|
|
45
|
-
# sys.stderr.write(r.text)
|
|
46
|
-
if r.status_code == 200:
|
|
47
|
-
projects = r.json()
|
|
48
|
-
names = map(lambda p: p["uid"], projects)
|
|
49
|
-
return list(names)
|
|
50
|
-
sys.stderr.write(f"Could not list projects due to {r.status_code}\n")
|
|
51
|
-
raise typer.Exit(0)
|
|
52
|
-
|
|
53
|
-
|
|
54
49
|
@app.command("list")
|
|
55
50
|
def list_recordings(
|
|
56
51
|
is_processing: bool = typer.Option(default=False, help="Use this option to see only those that are beeing processed or are invalid"),
|
|
@@ -59,13 +54,12 @@ def list_recordings(
|
|
|
59
54
|
"""
|
|
60
55
|
List all recording sessions in a project. You can choose to see all valid recordings (default) or use
|
|
61
56
|
--is-processing and you will get those that are currently beeing processed or that failed to be validated.
|
|
62
|
-
|
|
63
57
|
"""
|
|
64
58
|
|
|
65
59
|
if is_processing:
|
|
66
60
|
res = Rest.handle_get(f"/api/project/{project}/files/recording/processing", return_response=True)
|
|
67
61
|
json_res: List[Dict[str, Any]] = res.json()
|
|
68
|
-
|
|
62
|
+
print_generic_message(json.dumps(list(filter(lambda r: r["status"] == "RUNNING" or r["status"] == "FAILED", json_res))))
|
|
69
63
|
else:
|
|
70
64
|
Rest.handle_get(f"/api/project/{project}/files/recording")
|
|
71
65
|
|
|
@@ -78,14 +72,6 @@ def describe(
|
|
|
78
72
|
Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}")
|
|
79
73
|
|
|
80
74
|
|
|
81
|
-
# @app.command(help="Shows details about a specific recording in project")
|
|
82
|
-
# def copy(recording_session: str = typer.Argument(..., help="Recording session id"),
|
|
83
|
-
# target_project: str = typer.Option(..., help="Which project to copy the recording to"),
|
|
84
|
-
# project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
|
85
|
-
# Rest.handle_post(url=f"/api/project/{project}/files/recording/{recording_session}/copy",
|
|
86
|
-
# body=json.dumps({'projectUid': target_project}))
|
|
87
|
-
|
|
88
|
-
|
|
89
75
|
@app.command(name="import")
|
|
90
76
|
def import_as_recording(
|
|
91
77
|
uri: Annotated[URI, typer.Argument(help="Remote storage path", parser=URI, show_default=False)],
|
|
@@ -103,9 +89,7 @@ def import_as_recording(
|
|
|
103
89
|
body=json.dumps({"path": uri.path}),
|
|
104
90
|
)
|
|
105
91
|
|
|
106
|
-
|
|
107
|
-
f"Import started, you can track progress with 'remotive cloud recordings list --is-processing --project {project}'"
|
|
108
|
-
)
|
|
92
|
+
print_hint(f"Import started, you can track progress with 'remotive cloud recordings list --is-processing --project {project}'")
|
|
109
93
|
|
|
110
94
|
|
|
111
95
|
def do_start(name: str, project: str, api_key: str, return_response: bool = False) -> requests.Response:
|
|
@@ -142,10 +126,10 @@ def mount( # noqa: C901
|
|
|
142
126
|
elif r.status_code == 404:
|
|
143
127
|
r = do_start("personal", project, "", return_response=True)
|
|
144
128
|
if r.status_code != 200:
|
|
145
|
-
|
|
129
|
+
print_generic_error(r.text)
|
|
146
130
|
sys.exit(0)
|
|
147
131
|
else:
|
|
148
|
-
|
|
132
|
+
print_generic_error(f"Got http status code {r.status_code}")
|
|
149
133
|
raise typer.Exit(0)
|
|
150
134
|
else:
|
|
151
135
|
r = Rest.handle_get(url=f"/api/project/{project}/brokers/{broker}", return_response=True, allow_status_codes=[404])
|
|
@@ -154,10 +138,10 @@ def mount( # noqa: C901
|
|
|
154
138
|
if ensure_broker_started:
|
|
155
139
|
r = do_start(broker, project, "", return_response=True)
|
|
156
140
|
if r.status_code != 200:
|
|
157
|
-
|
|
141
|
+
print_generic_error(r.text)
|
|
158
142
|
sys.exit(1)
|
|
159
143
|
else:
|
|
160
|
-
|
|
144
|
+
print_generic_error(f"Broker {broker} not running")
|
|
161
145
|
sys.exit(1)
|
|
162
146
|
elif r.status_code != 200:
|
|
163
147
|
sys.stderr.write(f"Got http status code {r.status_code}")
|
|
@@ -175,8 +159,8 @@ def mount( # noqa: C901
|
|
|
175
159
|
return_response=True,
|
|
176
160
|
progress_label="Preparing recording on broker...",
|
|
177
161
|
)
|
|
178
|
-
|
|
179
|
-
|
|
162
|
+
print_unformatted_to_stderr("Successfully mounted recording on broker")
|
|
163
|
+
print_unformatted(json.dumps(broker_info))
|
|
180
164
|
|
|
181
165
|
|
|
182
166
|
@app.command(help="Downloads the specified recording file to disk")
|
|
@@ -198,9 +182,9 @@ def download_recording_file(
|
|
|
198
182
|
if get_signed_url_resp.status_code == 200:
|
|
199
183
|
# Next download the actual file
|
|
200
184
|
Rest.download_file(Path(recording_file_name), get_signed_url_resp.json()["downloadUrl"])
|
|
201
|
-
|
|
185
|
+
print_success(f"Downloaded {recording_file_name}")
|
|
202
186
|
else:
|
|
203
|
-
|
|
187
|
+
print_generic_error(get_signed_url_resp.text)
|
|
204
188
|
|
|
205
189
|
|
|
206
190
|
@app.command(name="delete")
|
|
@@ -267,10 +251,8 @@ def upload( # noqa: C901, PLR0912, PLR0915
|
|
|
267
251
|
if match:
|
|
268
252
|
upload_id = match.group(1)
|
|
269
253
|
else:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
)
|
|
273
|
-
ErrorPrinter.print_hint("Please make sure to use the latest version of RemotiveCLI")
|
|
254
|
+
print_generic_error("Something went wrong, please try again. Please contact RemotiveLabs support if this problem remains")
|
|
255
|
+
print_hint("Please make sure to use the latest version of RemotiveCLI")
|
|
274
256
|
sys.exit(1)
|
|
275
257
|
|
|
276
258
|
upload_response = Rest.upload_file_with_signed_url(
|
|
@@ -296,7 +278,6 @@ def upload( # noqa: C901, PLR0912, PLR0915
|
|
|
296
278
|
return "Finishing up..."
|
|
297
279
|
return "Processing..."
|
|
298
280
|
|
|
299
|
-
# print(response)
|
|
300
281
|
if 200 <= upload_response.status_code < 300:
|
|
301
282
|
# We need to print the error message outside the with Progress so the indicator is closed
|
|
302
283
|
error_message: Union[str, None] = None
|
|
@@ -319,17 +300,17 @@ def upload( # noqa: C901, PLR0912, PLR0915
|
|
|
319
300
|
if tracking_state["status"] == "FAILED":
|
|
320
301
|
error_message = f"Processing of uploaded file failed: {tracking_state['errors'][0]['message']}"
|
|
321
302
|
else:
|
|
322
|
-
|
|
303
|
+
print_success("File successfully uploaded")
|
|
323
304
|
break
|
|
324
305
|
else:
|
|
325
306
|
error_message = "Something went wrong, please try again. Please contact RemotiveLabs support if this problem remains"
|
|
326
307
|
break
|
|
327
308
|
if error_message is not None:
|
|
328
|
-
|
|
309
|
+
print_generic_error(error_message)
|
|
329
310
|
sys.exit(1)
|
|
330
311
|
|
|
331
312
|
else:
|
|
332
|
-
|
|
313
|
+
print_generic_error(f"Got status code: {upload_response.status_code} {upload_response.text}")
|
|
333
314
|
|
|
334
315
|
|
|
335
316
|
# TODO - Change to use Path for directory
|
|
@@ -412,8 +393,7 @@ def upload_broker_configuration(
|
|
|
412
393
|
if response is None:
|
|
413
394
|
return
|
|
414
395
|
if response.status_code != 200:
|
|
415
|
-
|
|
416
|
-
print(f"{response.text} - {response.status_code}")
|
|
396
|
+
print_generic_error(f"Failed to prepare configuration upload: {response.text} - {response.status_code}")
|
|
417
397
|
raise typer.Exit(1)
|
|
418
398
|
|
|
419
399
|
#
|
|
@@ -430,12 +410,10 @@ def upload_broker_configuration(
|
|
|
430
410
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
|
431
411
|
r = requests.put(url, open(path, "rb"), headers=headers, timeout=60)
|
|
432
412
|
if r.status_code != 200:
|
|
433
|
-
|
|
434
|
-
print(r.status_code)
|
|
435
|
-
print(r.text)
|
|
413
|
+
print_generic_error(f"Failed to upload broker configuration: {r.text} - {r.status_code}")
|
|
436
414
|
raise typer.Exit(1)
|
|
437
415
|
|
|
438
|
-
|
|
416
|
+
print_success(f"Uploaded broker configuration {broker_config_dir_name}")
|
|
439
417
|
|
|
440
418
|
|
|
441
419
|
@app.command(help="Downloads the specified broker configuration directory as zip file")
|
|
@@ -454,7 +432,7 @@ def download_broker_configuration(
|
|
|
454
432
|
if filename is not None:
|
|
455
433
|
with open(filename, "wb") as f:
|
|
456
434
|
f.write(r.content)
|
|
457
|
-
|
|
435
|
+
print_success(f"Downloaded file {filename}")
|
|
458
436
|
|
|
459
437
|
|
|
460
438
|
@app.command(help="Delete the specified broker configuration")
|
|
@@ -546,8 +524,8 @@ def _do_change_playback_mode( # noqa: PLR0912
|
|
|
546
524
|
broker_arg = ""
|
|
547
525
|
if broker_name is not None:
|
|
548
526
|
broker_arg = f" --broker {broker_name} --ensure-broker-started"
|
|
549
|
-
|
|
550
|
-
|
|
527
|
+
print_generic_error("You need to mount the recording before you play")
|
|
528
|
+
print_hint(f"remotive cloud recordings mount {recording_session}{broker_arg} --project {project}")
|
|
551
529
|
sys.exit(1)
|
|
552
530
|
|
|
553
531
|
broker_info = json.loads(response.text)
|
|
@@ -561,18 +539,14 @@ def _do_change_playback_mode( # noqa: PLR0912
|
|
|
561
539
|
with open(tmp, "r", encoding="utf8") as f:
|
|
562
540
|
json_context = json.loads(f.read())
|
|
563
541
|
if json_context["recordingSessionId"] != recording_session:
|
|
564
|
-
|
|
542
|
+
print_generic_error(
|
|
565
543
|
f"The recording id mounted is '{json_context['recordingSessionId']}' "
|
|
566
544
|
f"which not the same as you are trying to {mode}, use cmd below to mount this recording"
|
|
567
545
|
)
|
|
568
|
-
|
|
546
|
+
print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
|
569
547
|
sys.exit(1)
|
|
570
548
|
except grpc.RpcError as rpc_error:
|
|
571
|
-
|
|
572
|
-
# ErrorPrinter.print_generic_error(f"You must use mount to prepare a recording before you can use {mode}")
|
|
573
|
-
# ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
|
574
|
-
# else:
|
|
575
|
-
ErrorPrinter.print_grpc_error(rpc_error)
|
|
549
|
+
print_grpc_error(rpc_error)
|
|
576
550
|
sys.exit(1)
|
|
577
551
|
if mode == "pause":
|
|
578
552
|
broker.pause_play(files, True)
|
cli/cloud/recordings_playback.py
CHANGED
|
@@ -9,12 +9,10 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any, List, Union
|
|
10
10
|
|
|
11
11
|
import grpc
|
|
12
|
-
import rich
|
|
13
12
|
import typer
|
|
14
|
-
from rich import print as rich_print
|
|
15
13
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
16
14
|
|
|
17
|
-
from cli.
|
|
15
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_grpc_error, print_hint, print_unformatted
|
|
18
16
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
19
17
|
|
|
20
18
|
from ..broker.lib.broker import Broker, SubscribableSignal
|
|
@@ -127,11 +125,11 @@ def subscribe( # noqa: PLR0913
|
|
|
127
125
|
"""
|
|
128
126
|
if script is None:
|
|
129
127
|
if len(signal) == 0:
|
|
130
|
-
|
|
128
|
+
print_generic_error("You must use include at least one signal and one namespace or use script when subscribing")
|
|
131
129
|
sys.exit(1)
|
|
132
130
|
if script is not None:
|
|
133
131
|
if len(signal) > 0:
|
|
134
|
-
|
|
132
|
+
print_generic_error("You must must not specify --signal when using --script")
|
|
135
133
|
sys.exit(1)
|
|
136
134
|
|
|
137
135
|
broker_client = _get_broker_info(project, recording_session, broker, "subscribe")
|
|
@@ -139,24 +137,26 @@ def subscribe( # noqa: PLR0913
|
|
|
139
137
|
try:
|
|
140
138
|
if script is not None:
|
|
141
139
|
script_src = read_scripted_code_file(script)
|
|
142
|
-
broker_client.subscribe_on_script(script_src, lambda sig:
|
|
140
|
+
broker_client.subscribe_on_script(script_src, lambda sig: print_generic_message(json.dumps(list(sig))), on_change_only)
|
|
143
141
|
else:
|
|
144
142
|
|
|
145
143
|
def to_subscribable_signal(sig: str):
|
|
146
144
|
arr = sig.split(":")
|
|
147
145
|
if len(arr) != 2:
|
|
148
|
-
|
|
146
|
+
print_hint(f"--signal must have format namespace:signal ({sig})")
|
|
149
147
|
sys.exit(1)
|
|
150
148
|
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
|
151
149
|
|
|
152
150
|
signals_to_subscribe_to = list(map(to_subscribable_signal, signal))
|
|
153
|
-
broker_client.long_name_subscribe(
|
|
154
|
-
|
|
151
|
+
broker_client.long_name_subscribe(
|
|
152
|
+
signals_to_subscribe_to, lambda sig: print_generic_message(json.dumps(list(sig))), on_change_only
|
|
153
|
+
)
|
|
154
|
+
print_generic_message("Subscribing to signals, press Ctrl+C to exit")
|
|
155
155
|
except grpc.RpcError as rpc_error:
|
|
156
|
-
|
|
156
|
+
print_grpc_error(rpc_error)
|
|
157
157
|
|
|
158
158
|
except Exception as e:
|
|
159
|
-
|
|
159
|
+
print_generic_error(str(e))
|
|
160
160
|
sys.exit(1)
|
|
161
161
|
|
|
162
162
|
|
|
@@ -184,8 +184,8 @@ def _do_change_playback_mode( # noqa: C901, PLR0913, PLR0912
|
|
|
184
184
|
broker_arg = ""
|
|
185
185
|
if brokerstr is not None:
|
|
186
186
|
broker_arg = f" --broker {brokerstr} --ensure-broker-started"
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
print_generic_error("You need to mount the recording before you play")
|
|
188
|
+
print_hint(f"remotive cloud recordings mount {recording_session}{broker_arg} --project {project}")
|
|
189
189
|
sys.exit(1)
|
|
190
190
|
|
|
191
191
|
broker_info = json.loads(response.text)
|
|
@@ -216,7 +216,7 @@ def _track_progress(broker: Broker, repeat: bool, files: List[Any]) -> None:
|
|
|
216
216
|
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
|
217
217
|
t = p.add_task("label", total=1)
|
|
218
218
|
if repeat:
|
|
219
|
-
|
|
219
|
+
print_unformatted(":point_right: Keep this command running in terminal to keep the recording play with repeat")
|
|
220
220
|
with p:
|
|
221
221
|
|
|
222
222
|
def print_progress(offset: int, total: int, current_mode: str) -> None:
|
|
@@ -238,18 +238,18 @@ def _verify_recording_on_broker(broker: Broker, recording_session: str, mode: st
|
|
|
238
238
|
with open(tmp, "r", encoding="utf8") as f:
|
|
239
239
|
json_context = json.loads(f.read())
|
|
240
240
|
if json_context["recordingSessionId"] != recording_session:
|
|
241
|
-
|
|
241
|
+
print_generic_error(
|
|
242
242
|
f"The recording id mounted is '{json_context['recordingSessionId']}' "
|
|
243
243
|
f"which not the same as you are trying to {mode}, use cmd below to mount this recording"
|
|
244
244
|
)
|
|
245
|
-
|
|
245
|
+
print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
|
246
246
|
sys.exit(1)
|
|
247
247
|
except grpc.RpcError as rpc_error:
|
|
248
248
|
if rpc_error.code() == grpc.StatusCode.NOT_FOUND:
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
print_generic_error(f"You must use mount to prepare a recording before you can use {mode}")
|
|
250
|
+
print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
|
251
251
|
else:
|
|
252
|
-
|
|
252
|
+
print_grpc_error(rpc_error)
|
|
253
253
|
sys.exit(1)
|
|
254
254
|
|
|
255
255
|
|
|
@@ -260,14 +260,14 @@ def _get_broker_info(project: str, recording_session: str, broker: Union[str, No
|
|
|
260
260
|
broker_name = broker if broker is not None else "personal"
|
|
261
261
|
response = Rest.handle_get(f"/api/project/{project}/brokers/{broker_name}", return_response=True, allow_status_codes=[404])
|
|
262
262
|
if response is None:
|
|
263
|
-
|
|
263
|
+
print_generic_error(f"No response from: /api/project/{project}/brokers/{broker_name}")
|
|
264
264
|
sys.exit(1)
|
|
265
265
|
if response.status_code == 404:
|
|
266
266
|
broker_arg = ""
|
|
267
267
|
if broker is not None:
|
|
268
268
|
broker_arg = f"--broker {broker} --ensure-broker-started"
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
print_generic_error("You need to mount the recording before you play")
|
|
270
|
+
print_hint(f"remotive cloud recordings mount {recording_session} {broker_arg} --project {project}")
|
|
271
271
|
sys.exit(1)
|
|
272
272
|
broker_info = json.loads(response.text)
|
|
273
273
|
broker_client = Broker(broker_info["url"], None)
|
cli/cloud/resumable_upload.py
CHANGED
|
@@ -8,7 +8,7 @@ from typing import Dict
|
|
|
8
8
|
import requests
|
|
9
9
|
from rich.progress import wrap_file
|
|
10
10
|
|
|
11
|
-
from
|
|
11
|
+
from cli.utils.console import print_generic_error, print_success
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def __get_uploaded_bytes(upload_url: str) -> int:
|
|
@@ -40,7 +40,7 @@ def with_resumable_upload_signed_url(signed_url: str, source_file_name: str, con
|
|
|
40
40
|
headers = {"x-goog-resumable": "start", "content-type": content_type}
|
|
41
41
|
response = requests.post(signed_url, headers=headers, timeout=60)
|
|
42
42
|
if response.status_code not in (200, 201, 308):
|
|
43
|
-
|
|
43
|
+
print_generic_error(f"Failed to upload file: {response.status_code} - {response.text}")
|
|
44
44
|
sys.exit(1)
|
|
45
45
|
|
|
46
46
|
upload_url = response.headers["Location"]
|
|
@@ -61,10 +61,10 @@ def with_resumable_upload_signed_url(signed_url: str, source_file_name: str, con
|
|
|
61
61
|
headers = {"Content-Range": f"bytes {chunk_start}-{chunk_end}/{file_size}"}
|
|
62
62
|
response = requests.put(upload_url, headers=headers, data=chunk, timeout=60)
|
|
63
63
|
if response.status_code not in (200, 201, 308):
|
|
64
|
-
|
|
64
|
+
print_generic_error(f"Failed to upload file: {response.status_code} - {response.text}")
|
|
65
65
|
sys.exit(1)
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
print_success(f"File {source_file_name} uploaded successfully.")
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def upload_signed_url(signed_url: str, source_file_name: Path, headers: Dict[str, str]) -> None:
|
|
@@ -81,7 +81,7 @@ def upload_signed_url(signed_url: str, source_file_name: Path, headers: Dict[str
|
|
|
81
81
|
with wrap_file(file, os.stat(source_file_name).st_size, description=f"Uploading {source_file_name}...") as f:
|
|
82
82
|
response = requests.put(signed_url, headers=headers, timeout=60, data=f)
|
|
83
83
|
if response.status_code not in (200, 201, 308):
|
|
84
|
-
|
|
84
|
+
print_generic_error(f"Failed to upload file: {response.status_code} - {response.text}")
|
|
85
85
|
sys.exit(1)
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
print_success(f"File {source_file_name} uploaded successfully.")
|
|
@@ -4,6 +4,7 @@ import typer
|
|
|
4
4
|
|
|
5
5
|
from cli.settings import settings
|
|
6
6
|
from cli.typer import typer_utils
|
|
7
|
+
from cli.utils.console import print_generic_message, print_success
|
|
7
8
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
8
9
|
|
|
9
10
|
app = typer_utils.create_typer()
|
|
@@ -23,8 +24,8 @@ def create(
|
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
sat = settings.add_service_account_token(response.text)
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
print_success(f"Service account access token added: {sat.name}")
|
|
28
|
+
print_generic_message("This file contains secrets and must be kept safe")
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@app.command(name="list", help="List service account access tokens")
|
cli/cloud/storage/cmd.py
CHANGED
|
@@ -7,15 +7,14 @@ from cli.cloud.storage.copy import copy
|
|
|
7
7
|
from cli.cloud.storage.uri_or_path import UriOrPath
|
|
8
8
|
from cli.cloud.storage.uri_or_path import uri as uri_parser
|
|
9
9
|
from cli.cloud.uri import URI, InvalidURIError, JoinURIError
|
|
10
|
-
from cli.errors import ErrorPrinter
|
|
11
10
|
from cli.typer import typer_utils
|
|
11
|
+
from cli.utils.console import print_hint
|
|
12
12
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
13
13
|
|
|
14
14
|
HELP = """
|
|
15
15
|
Manage files ([yellow]Beta feature not available for all customers[/yellow])
|
|
16
16
|
|
|
17
17
|
Copy file from local to remote storage and vice versa, list and delete files.
|
|
18
|
-
|
|
19
18
|
"""
|
|
20
19
|
|
|
21
20
|
app = typer_utils.create_typer(rich_markup_mode="rich", help=HELP)
|
|
@@ -73,5 +72,5 @@ def copy_file(
|
|
|
73
72
|
try:
|
|
74
73
|
return copy(project=project, source=source.value, dest=dest.value, overwrite=overwrite)
|
|
75
74
|
except (InvalidURIError, JoinURIError, ValueError, FileNotFoundError, FileExistsError) as e:
|
|
76
|
-
|
|
75
|
+
print_hint(str(e))
|
|
77
76
|
sys.exit(1)
|
cli/cloud/storage/copy.py
CHANGED
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from cli.cloud.resumable_upload import upload_signed_url
|
|
7
7
|
from cli.cloud.uri import URI
|
|
8
|
+
from cli.utils.console import print_success
|
|
8
9
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
9
10
|
|
|
10
11
|
_RCS_STORAGE_PATH = "/api/project/{project}/files/storage{path}"
|
|
@@ -63,7 +64,7 @@ def _upload_single_file(source: Path, target_uri: URI, project: str, overwrite:
|
|
|
63
64
|
headers = json_res["headers"]
|
|
64
65
|
upload_signed_url(url, source, headers)
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
print_success(f"Uploaded {source} to {target_uri.path}")
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
def _download(source: URI, dest: Path, project: str, overwrite: bool = False) -> None:
|
cli/connect/connect.py
CHANGED
|
@@ -8,8 +8,8 @@ import typer
|
|
|
8
8
|
from typing_extensions import List
|
|
9
9
|
|
|
10
10
|
from cli.broker.lib.broker import SubscribableSignal
|
|
11
|
-
from cli.errors import ErrorPrinter
|
|
12
11
|
from cli.typer import typer_utils
|
|
12
|
+
from cli.utils.console import print_hint
|
|
13
13
|
|
|
14
14
|
from .protopie import protopie as ppie
|
|
15
15
|
|
|
@@ -81,18 +81,18 @@ def protopie( # noqa: PLR0913
|
|
|
81
81
|
"""
|
|
82
82
|
|
|
83
83
|
if len(signal) > 0 and config is not None:
|
|
84
|
-
|
|
84
|
+
print_hint("You must choose either --signal or --config, not both")
|
|
85
85
|
sys.exit(1)
|
|
86
86
|
|
|
87
87
|
if len(signal) == 0 and config is None:
|
|
88
|
-
|
|
88
|
+
print_hint("You must choose either --signal or --config")
|
|
89
89
|
sys.exit(1)
|
|
90
90
|
|
|
91
91
|
def to_subscribable_signal(sig: str) -> SubscribableSignal:
|
|
92
92
|
arr = sig.split(":")
|
|
93
93
|
|
|
94
94
|
if len(arr) != 2:
|
|
95
|
-
|
|
95
|
+
print_hint(f"--signal must have format namespace:signal ({sig})")
|
|
96
96
|
sys.exit(1)
|
|
97
97
|
|
|
98
98
|
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
cli/connect/protopie/protopie.py
CHANGED
|
@@ -5,27 +5,22 @@ import json
|
|
|
5
5
|
import os
|
|
6
6
|
import sys
|
|
7
7
|
import time
|
|
8
|
-
import traceback
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
from typing import Any, Dict, List, Tuple, Union
|
|
11
10
|
|
|
12
11
|
import grpc
|
|
13
12
|
import socketio
|
|
14
|
-
from rich import print as pretty_print
|
|
15
|
-
from rich.console import Console
|
|
16
13
|
from socketio.exceptions import ConnectionError as SocketIoConnectionError
|
|
17
14
|
|
|
18
15
|
from cli.broker.lib.broker import SubscribableSignal
|
|
19
16
|
from cli.broker.lib.client import BrokerException, Client, SignalIdentifier, SignalsInFrame
|
|
20
|
-
from cli.errors import ErrorPrinter
|
|
21
17
|
from cli.settings import settings
|
|
18
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_success, print_unformatted_to_stderr
|
|
22
19
|
|
|
23
20
|
PP_CONNECT_APP_NAME = "RemotiveBridge"
|
|
24
21
|
|
|
25
22
|
io = socketio.Client()
|
|
26
23
|
|
|
27
|
-
err_console = Console(stderr=True)
|
|
28
|
-
|
|
29
24
|
_has_received_signal = False
|
|
30
25
|
is_connected = False
|
|
31
26
|
config_path: Path
|
|
@@ -35,7 +30,7 @@ broker: Any
|
|
|
35
30
|
|
|
36
31
|
@io.on("connect")
|
|
37
32
|
def on_connect() -> None:
|
|
38
|
-
|
|
33
|
+
print_success("Connected to ProtoPie Connect")
|
|
39
34
|
io.emit("ppBridgeApp", {"name": PP_CONNECT_APP_NAME})
|
|
40
35
|
io.emit("PLUGIN_STARTED", {"name": PP_CONNECT_APP_NAME})
|
|
41
36
|
|
|
@@ -72,8 +67,8 @@ def get_signal_name(expression: str, s_name: str) -> str:
|
|
|
72
67
|
sig_name = eval(f"s_name.{expression}")
|
|
73
68
|
return str(sig_name)
|
|
74
69
|
except Exception as e:
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
print_generic_error(f"Failed to evaluate your python expression {expression}")
|
|
71
|
+
print_unformatted_to_stderr(e)
|
|
77
72
|
# This was the only way I could make this work, exiting on another thread than main
|
|
78
73
|
os._exit(1)
|
|
79
74
|
else:
|
|
@@ -91,7 +86,7 @@ def _connect_to_broker(
|
|
|
91
86
|
def on_signals(frame: SignalsInFrame) -> None:
|
|
92
87
|
global _has_received_signal # noqa: PLW0603
|
|
93
88
|
if not _has_received_signal:
|
|
94
|
-
|
|
89
|
+
print_generic_message("Bridge-app is properly receiving signals, you are good to go :thumbsup:")
|
|
95
90
|
_has_received_signal = True
|
|
96
91
|
|
|
97
92
|
for s in frame:
|
|
@@ -114,42 +109,42 @@ def grpc_connect(
|
|
|
114
109
|
on_signals: Any, signals_to_subscribe_to: Union[List[SignalIdentifier], None] = None, on_change_only: bool = False
|
|
115
110
|
) -> None:
|
|
116
111
|
try:
|
|
117
|
-
|
|
112
|
+
print_generic_message("Connecting and subscribing to broker...")
|
|
118
113
|
subscription = None
|
|
119
114
|
client = Client(client_id="cli")
|
|
120
115
|
client.connect(url=broker, api_key=x_api_key)
|
|
121
116
|
client.on_signals = on_signals
|
|
122
117
|
|
|
123
118
|
if signals_to_subscribe_to is None:
|
|
124
|
-
print
|
|
119
|
+
# TODO: use logs instead of print?
|
|
120
|
+
print_generic_error("No signals to subscribe to")
|
|
125
121
|
return
|
|
126
122
|
subscription = client.subscribe(signals_to_subscribe_to=signals_to_subscribe_to, changed_values_only=on_change_only)
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
print_generic_message("Subscription to broker completed")
|
|
124
|
+
print_generic_message("Waiting for signals...")
|
|
129
125
|
|
|
130
126
|
while True:
|
|
131
127
|
time.sleep(1)
|
|
132
128
|
|
|
133
129
|
except grpc.RpcError as e:
|
|
134
|
-
|
|
130
|
+
print_generic_error("Problems connecting or subscribing")
|
|
135
131
|
if isinstance(e, grpc.Call):
|
|
136
|
-
|
|
132
|
+
print_generic_error(f"{e.code()} - {e.details()}")
|
|
137
133
|
else:
|
|
138
|
-
|
|
134
|
+
print_generic_error(e)
|
|
139
135
|
|
|
140
136
|
except BrokerException as e:
|
|
141
|
-
|
|
137
|
+
print_generic_error(e)
|
|
142
138
|
if subscription is not None:
|
|
143
139
|
subscription.cancel()
|
|
144
140
|
|
|
145
141
|
except KeyboardInterrupt:
|
|
146
|
-
|
|
142
|
+
print_generic_message("Keyboard interrupt received. Closing subscription.")
|
|
147
143
|
if subscription is not None:
|
|
148
144
|
subscription.cancel()
|
|
149
145
|
|
|
150
146
|
except Exception as e:
|
|
151
|
-
|
|
152
|
-
err_console.print(f":boom: {e}")
|
|
147
|
+
print_generic_error(e)
|
|
153
148
|
|
|
154
149
|
|
|
155
150
|
def do_connect( # noqa: PLR0913
|
|
@@ -168,7 +163,7 @@ def do_connect( # noqa: PLR0913
|
|
|
168
163
|
|
|
169
164
|
if broker_url.startswith("https"):
|
|
170
165
|
if api_key is None:
|
|
171
|
-
|
|
166
|
+
print_generic_message("No --api-key, reading token from file")
|
|
172
167
|
x_api_key = settings.get_active_token_secret()
|
|
173
168
|
else:
|
|
174
169
|
x_api_key = api_key
|
|
@@ -176,18 +171,15 @@ def do_connect( # noqa: PLR0913
|
|
|
176
171
|
x_api_key = api_key
|
|
177
172
|
try:
|
|
178
173
|
io.connect(address)
|
|
179
|
-
# if config is None:
|
|
180
|
-
# raise ValueError("Config is None")
|
|
181
174
|
config_path = config
|
|
182
175
|
while is_connected is None:
|
|
183
176
|
time.sleep(1)
|
|
184
|
-
# if expression is not None:
|
|
185
177
|
_connect_to_broker(signals_to_subscribe_to=signals, config=config, expression=expression, on_change_only=on_change_only)
|
|
186
178
|
except SocketIoConnectionError as e:
|
|
187
|
-
|
|
188
|
-
|
|
179
|
+
print_generic_error("Failed to connect to ProtoPie Connect")
|
|
180
|
+
print_unformatted_to_stderr(e)
|
|
189
181
|
sys.exit(1)
|
|
190
182
|
except Exception as e:
|
|
191
|
-
|
|
192
|
-
|
|
183
|
+
print_generic_error("Unexpected error")
|
|
184
|
+
print_unformatted_to_stderr(e)
|
|
193
185
|
sys.exit(1)
|