remotivelabs-cli 0.3.3__py3-none-any.whl → 0.3.5__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/signals.py +1 -1
- cli/settings/core.py +12 -1
- cli/topology/cmd.py +43 -9
- cli/topology/start_trial.py +46 -17
- cli/utils/rest_helper.py +3 -1
- {remotivelabs_cli-0.3.3.dist-info → remotivelabs_cli-0.3.5.dist-info}/METADATA +1 -1
- {remotivelabs_cli-0.3.3.dist-info → remotivelabs_cli-0.3.5.dist-info}/RECORD +10 -10
- {remotivelabs_cli-0.3.3.dist-info → remotivelabs_cli-0.3.5.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.3.3.dist-info → remotivelabs_cli-0.3.5.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.3.3.dist-info → remotivelabs_cli-0.3.5.dist-info}/entry_points.txt +0 -0
cli/broker/signals.py
CHANGED
|
@@ -198,7 +198,7 @@ def namespaces(
|
|
|
198
198
|
|
|
199
199
|
@app.command()
|
|
200
200
|
def frame_distribution(
|
|
201
|
-
url: str = typer.Option(
|
|
201
|
+
url: str = typer.Option(DEFAULT_GRPC_URL, help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
|
202
202
|
api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
|
203
203
|
namespace: str = typer.Option(..., help="Namespace"),
|
|
204
204
|
) -> None:
|
cli/settings/core.py
CHANGED
|
@@ -22,6 +22,10 @@ CONFIG_DIR_PATH = Path.home() / ".config" / "remotive"
|
|
|
22
22
|
CLI_CONFIG_FILE_NAME = "config.json"
|
|
23
23
|
CLI_INTERNAL_STATE_FILE_NAME = "app-state.json"
|
|
24
24
|
|
|
25
|
+
TOKEN_ENV = "REMOTIVE_CLOUD_AUTH_TOKEN"
|
|
26
|
+
# Deprecated in favour of name used in topology-cli
|
|
27
|
+
DEPR_TOKEN_ENV = "REMOTIVE_CLOUD_ACCESS_TOKEN"
|
|
28
|
+
|
|
25
29
|
|
|
26
30
|
class InvalidSettingsFilePathError(Exception):
|
|
27
31
|
"""Raised when trying to access an invalid settings file or file path"""
|
|
@@ -91,8 +95,15 @@ class Settings:
|
|
|
91
95
|
|
|
92
96
|
def get_active_token(self) -> str | None:
|
|
93
97
|
"""
|
|
94
|
-
Get the token secret for the current active account
|
|
98
|
+
Get the token secret for the current active account or token specified by env variable
|
|
95
99
|
"""
|
|
100
|
+
|
|
101
|
+
token = os.environ[DEPR_TOKEN_ENV] if DEPR_TOKEN_ENV in os.environ else None
|
|
102
|
+
if not token:
|
|
103
|
+
token = os.environ[TOKEN_ENV] if TOKEN_ENV in os.environ else None
|
|
104
|
+
if token:
|
|
105
|
+
return token
|
|
106
|
+
|
|
96
107
|
token_file = self.get_active_token_file()
|
|
97
108
|
return token_file.token if token_file else None
|
|
98
109
|
|
cli/topology/cmd.py
CHANGED
|
@@ -5,10 +5,12 @@ import typer
|
|
|
5
5
|
from cli.topology.start_trial import (
|
|
6
6
|
MissingOrganizationError,
|
|
7
7
|
NoActiveAccountError,
|
|
8
|
+
NoOrganizationOrPermissionError,
|
|
8
9
|
NotAuthorizedError,
|
|
9
10
|
NotAuthorizedToStartTrialError,
|
|
10
11
|
NotSignedInError,
|
|
11
12
|
SubscriptionExpiredError,
|
|
13
|
+
get_organization_and_account,
|
|
12
14
|
start_trial,
|
|
13
15
|
)
|
|
14
16
|
from cli.typer import typer_utils
|
|
@@ -30,12 +32,34 @@ def start_trial_cmd( # noqa: C901
|
|
|
30
32
|
|
|
31
33
|
You can read more at https://docs.remotivelabs.com/docs/remotive-topology.
|
|
32
34
|
"""
|
|
35
|
+
|
|
33
36
|
try:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
(valid_organization, _) = get_organization_and_account(organization_uid=organization)
|
|
38
|
+
ok_to_start_trial = typer.confirm(
|
|
39
|
+
f"You are about to start trial of RemotiveTopology for {valid_organization.display_name}"
|
|
40
|
+
f" (uid={valid_organization.uid}), continue?",
|
|
41
|
+
default=True,
|
|
42
|
+
)
|
|
43
|
+
if not ok_to_start_trial:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
(subscription, created) = start_trial(organization)
|
|
47
|
+
|
|
48
|
+
def print_start_trial_result() -> None:
|
|
49
|
+
assert subscription.type in ("trial", "paid"), f"Unexpected subscription type: {subscription.type}"
|
|
50
|
+
kind = None
|
|
51
|
+
status = None
|
|
52
|
+
if subscription.type == "trial":
|
|
53
|
+
status = "started now" if created else "is already active"
|
|
54
|
+
kind = "trial subscription"
|
|
55
|
+
elif subscription.type == "paid":
|
|
56
|
+
status = "started now" if created else "is already started"
|
|
57
|
+
kind = "subscription"
|
|
58
|
+
|
|
59
|
+
end_text = subscription.end_date or "Never"
|
|
60
|
+
print_generic_message(f"RemotiveTopology {kind} for {valid_organization.display_name} {status}, expires {end_text}")
|
|
61
|
+
|
|
62
|
+
print_start_trial_result()
|
|
39
63
|
|
|
40
64
|
except NotSignedInError:
|
|
41
65
|
print_generic_error(
|
|
@@ -46,7 +70,7 @@ def start_trial_cmd( # noqa: C901
|
|
|
46
70
|
|
|
47
71
|
except NoActiveAccountError:
|
|
48
72
|
print_hint(
|
|
49
|
-
"You have not
|
|
73
|
+
"You have not activated your account, please run [bold]remotive cloud auth activate[/bold] to choose an account"
|
|
50
74
|
"or [bold]remotive cloud auth login[/bold] to sign-in"
|
|
51
75
|
)
|
|
52
76
|
raise typer.Exit(3)
|
|
@@ -63,15 +87,25 @@ def start_trial_cmd( # noqa: C901
|
|
|
63
87
|
raise typer.Exit(5)
|
|
64
88
|
|
|
65
89
|
except NotAuthorizedToStartTrialError as e:
|
|
66
|
-
print_generic_error(f"You are not allowed to start-trial topology in organization {e.organization}")
|
|
90
|
+
print_generic_error(f"You are not allowed to start-trial topology in organization {e.organization.display_name}.")
|
|
67
91
|
raise typer.Exit(6)
|
|
68
92
|
|
|
69
93
|
except SubscriptionExpiredError as e:
|
|
70
94
|
if e.subscription.type == "trial":
|
|
71
|
-
print_generic_error(
|
|
95
|
+
print_generic_error(
|
|
96
|
+
f"RemotiveTopology trial in {e.organization.display_name} expired"
|
|
97
|
+
f" {e.subscription.end_date}, please contact support@remotivelabs.com"
|
|
98
|
+
)
|
|
72
99
|
raise typer.Exit(7)
|
|
73
100
|
|
|
74
|
-
print_generic_error(
|
|
101
|
+
print_generic_error(
|
|
102
|
+
f"RemotiveTopology subscription in {e.organization.display_name} has expired"
|
|
103
|
+
f" {e.subscription.end_date}, please contact support@remotivelabs.com"
|
|
104
|
+
)
|
|
105
|
+
raise typer.Exit(7)
|
|
106
|
+
|
|
107
|
+
except NoOrganizationOrPermissionError as e:
|
|
108
|
+
print_generic_error(f"Organization id {e.organization} does not exist or you do not have permission to access it.")
|
|
75
109
|
raise typer.Exit(7)
|
|
76
110
|
|
|
77
111
|
except Exception as e:
|
cli/topology/start_trial.py
CHANGED
|
@@ -26,6 +26,14 @@ class NotAuthorizedError(Exception):
|
|
|
26
26
|
self.account = account
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
class NoOrganizationOrPermissionError(Exception):
|
|
30
|
+
"""Raised when the user is trying to access an organization that it does not have access to or it does not exist"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, account: Account, organization_uid: str):
|
|
33
|
+
self.account = account
|
|
34
|
+
self.organization = organization_uid
|
|
35
|
+
|
|
36
|
+
|
|
29
37
|
class MissingOrganizationError(Exception):
|
|
30
38
|
"""Raised when the user has not specified an organization and no default organization is set"""
|
|
31
39
|
|
|
@@ -33,7 +41,7 @@ class MissingOrganizationError(Exception):
|
|
|
33
41
|
class NotAuthorizedToStartTrialError(Exception):
|
|
34
42
|
"""Raised when the user is not authorized to start a topology trial"""
|
|
35
43
|
|
|
36
|
-
def __init__(self, account: Account, organization:
|
|
44
|
+
def __init__(self, account: Account, organization: Organization):
|
|
37
45
|
self.account = account
|
|
38
46
|
self.organization = organization
|
|
39
47
|
|
|
@@ -41,8 +49,15 @@ class NotAuthorizedToStartTrialError(Exception):
|
|
|
41
49
|
class SubscriptionExpiredError(Exception):
|
|
42
50
|
"""Raised when the subscription has expired"""
|
|
43
51
|
|
|
44
|
-
def __init__(self, subscription: Subscription):
|
|
52
|
+
def __init__(self, subscription: Subscription, organization: Organization):
|
|
45
53
|
self.subscription = subscription
|
|
54
|
+
self.organization = organization
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class Organization:
|
|
59
|
+
uid: str
|
|
60
|
+
display_name: str
|
|
46
61
|
|
|
47
62
|
|
|
48
63
|
@dataclass
|
|
@@ -67,12 +82,7 @@ class Subscription:
|
|
|
67
82
|
)
|
|
68
83
|
|
|
69
84
|
|
|
70
|
-
def
|
|
71
|
-
"""
|
|
72
|
-
Start a 30 day trial subscription for running RemotiveTopology.
|
|
73
|
-
|
|
74
|
-
# TODO: move authentication (and basic authorization) to a logged_in decorator
|
|
75
|
-
"""
|
|
85
|
+
def get_organization_and_account(organization_uid: str | None = None) -> tuple[Organization, Account]:
|
|
76
86
|
active_account = settings.get_active_account()
|
|
77
87
|
active_token_file = settings.get_active_token_file()
|
|
78
88
|
|
|
@@ -84,22 +94,41 @@ def start_trial(organization: str | None = None) -> Subscription:
|
|
|
84
94
|
if not RestHelper.has_access("/api/whoami"):
|
|
85
95
|
raise NotAuthorizedError(account=active_account)
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
if not
|
|
97
|
+
org_uid = organization_uid or active_account.default_organization
|
|
98
|
+
if not org_uid:
|
|
89
99
|
raise MissingOrganizationError()
|
|
90
100
|
|
|
91
|
-
|
|
101
|
+
response = RestHelper.handle_get(f"/api/bu/{organization_uid}", return_response=True, allow_status_codes=[403, 404])
|
|
102
|
+
if response.status_code in (403, 404):
|
|
103
|
+
raise NoOrganizationOrPermissionError(account=active_account, organization_uid=org_uid)
|
|
104
|
+
|
|
105
|
+
display_name = response.json()["displayName"]
|
|
106
|
+
|
|
107
|
+
return Organization(uid=org_uid, display_name=display_name), active_account
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def start_trial(organization_uid: str | None = None) -> tuple[Subscription, bool]:
|
|
111
|
+
"""
|
|
112
|
+
Start a 30 day trial subscription for running RemotiveTopology,
|
|
113
|
+
If a trial is already started in this organization, returns the existing trial instead.
|
|
114
|
+
Returns (subscription, created_now).
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
(organization, active_account) = get_organization_and_account(organization_uid)
|
|
118
|
+
|
|
119
|
+
res = RestHelper.handle_get(f"/api/bu/{organization.uid}/features/topology", return_response=True, allow_status_codes=[403, 404])
|
|
92
120
|
if res.status_code == 403:
|
|
93
|
-
raise NotAuthorizedToStartTrialError(account=active_account, organization=
|
|
121
|
+
raise NotAuthorizedToStartTrialError(account=active_account, organization=organization)
|
|
122
|
+
|
|
123
|
+
created_now = False
|
|
94
124
|
if res.status_code == 404:
|
|
95
|
-
created = RestHelper.handle_post(f"/api/bu/{
|
|
125
|
+
created = RestHelper.handle_post(f"/api/bu/{organization.uid}/features/topology", return_response=True)
|
|
96
126
|
subscription = Subscription.from_dict(created.json())
|
|
127
|
+
created_now = True
|
|
97
128
|
else:
|
|
98
|
-
# 200 OK means we already have a valid subscription
|
|
99
129
|
subscription = Subscription.from_dict(res.json())
|
|
100
130
|
|
|
101
|
-
# check subscription validity
|
|
102
131
|
if subscription.end_date < datetime.datetime.now().date():
|
|
103
|
-
raise SubscriptionExpiredError(subscription=subscription)
|
|
132
|
+
raise SubscriptionExpiredError(subscription=subscription, organization=organization)
|
|
104
133
|
|
|
105
|
-
return subscription
|
|
134
|
+
return subscription, created_now
|
cli/utils/rest_helper.py
CHANGED
|
@@ -85,6 +85,7 @@ class RestHelper:
|
|
|
85
85
|
TODO: remove setting org, as we already set the default organization as env in remotive.py?
|
|
86
86
|
TODO: don't sys.exit, raise error instead
|
|
87
87
|
"""
|
|
88
|
+
|
|
88
89
|
if "REMOTIVE_CLOUD_ORGANIZATION" not in os.environ:
|
|
89
90
|
active_account = settings.get_active_account()
|
|
90
91
|
if active_account:
|
|
@@ -94,7 +95,8 @@ class RestHelper:
|
|
|
94
95
|
|
|
95
96
|
token = access_token
|
|
96
97
|
if not token:
|
|
97
|
-
token =
|
|
98
|
+
token = settings.get_active_token()
|
|
99
|
+
|
|
98
100
|
if not token:
|
|
99
101
|
if quiet:
|
|
100
102
|
return
|
|
@@ -15,7 +15,7 @@ cli/broker/licenses.py,sha256=jIuLB2qBGflzSjm952CBnErpzs7iIkmEgx9L8GDAPNc,4021
|
|
|
15
15
|
cli/broker/playback.py,sha256=1N8jTTgd2rw3VRbpyzP3I-06ritceeYxc_uEKst-8j8,4110
|
|
16
16
|
cli/broker/record.py,sha256=FjpeCbd1pUcDcpgF7QGLuBpecv4x4BFOGwELO8De3go,1440
|
|
17
17
|
cli/broker/scripting.py,sha256=lD8GuoVa8Ku4E2BUAq-USfMqG4utZMRgr61jePogykM,3747
|
|
18
|
-
cli/broker/signals.py,sha256=
|
|
18
|
+
cli/broker/signals.py,sha256=ldZerF4DngjDHsPjI5s90SjBhFhCqUaGgmF9baEz574,8258
|
|
19
19
|
cli/cloud/__init__.py,sha256=xFaDg2zho_glvDBDB0_8HRNICVwHeYFEz-yMhzTcCjI,987
|
|
20
20
|
cli/cloud/auth/__init__.py,sha256=MtQ01-n8CgZb9Y_SvxwZUgj44Yo0dFAU3_XwhQiUYtw,54
|
|
21
21
|
cli/cloud/auth/cmd.py,sha256=lv9D0Q7G1RgI2IO1vkz_VVBA_STCPmdJax3QkeiMwg4,3968
|
|
@@ -44,7 +44,7 @@ cli/connect/protopie/protopie.py,sha256=0S-GCNiWpTvXzJEsFSht9HoziSzu_6hXZMvTp4xA
|
|
|
44
44
|
cli/remotive.py,sha256=EwgjtTjR5EmGo39PFpbF74v2nm3K2E9A1kon89R2KiE,3854
|
|
45
45
|
cli/settings/__init__.py,sha256=JsMr0E_hsM6IRHYeJUrlLBGyKnPdR4cDJd08-TjX274,665
|
|
46
46
|
cli/settings/config_file.py,sha256=QwsrVGB7JTqFNXlLkbWVcRSveW0HsKzU6Jl8mHqjdO8,3586
|
|
47
|
-
cli/settings/core.py,sha256=
|
|
47
|
+
cli/settings/core.py,sha256=9VpnjRxMhJ6MooX3N_qWqR3cD6yjLjn7sMvbySUuAR4,11405
|
|
48
48
|
cli/settings/migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
cli/settings/migration/migrate_all_token_files.py,sha256=xoVvAqn_tGskEW148uf3xZx1mpJKUnERMTcBo0nkCnI,3010
|
|
50
50
|
cli/settings/migration/migrate_config_file.py,sha256=S8kyn3ZXbkej2TRLPcVDcYpvk2iW6kGo38OIutEZayo,2217
|
|
@@ -58,17 +58,17 @@ cli/tools/can/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
58
58
|
cli/tools/can/can.py,sha256=ovTqzsjJP-z9H5RiJ3f_OXMpELqM3d09JY4JfPpT3-Q,2171
|
|
59
59
|
cli/tools/tools.py,sha256=jhLfrFDqkmWV3eBAzNwBf6WgDGrz7sOhgVCia36Twn8,232
|
|
60
60
|
cli/topology/__init__.py,sha256=MyorYLmC2wAd_z5GdQENEYLgoCiNYgwQ16BOX93IBtI,52
|
|
61
|
-
cli/topology/cmd.py,sha256=
|
|
62
|
-
cli/topology/start_trial.py,sha256=
|
|
61
|
+
cli/topology/cmd.py,sha256=TCSwURxTmAyU4Lvu-Rk8ymPardmAARuFEn-kTckksvw,4408
|
|
62
|
+
cli/topology/start_trial.py,sha256=b0PWAFS9Q67kCLq9zZ-t6vl0c3WMiF4BF4nN5yqyZ-I,4538
|
|
63
63
|
cli/typer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
64
|
cli/typer/typer_utils.py,sha256=eUrVOEOKY_MnfiJMAxvEurP1Q79z9pq83mJjocP8lhI,686
|
|
65
65
|
cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
66
|
cli/utils/console.py,sha256=JF39S9oS1lehFexjdLQKp17oINNvFZJLg_M2SDCjkvA,1754
|
|
67
|
-
cli/utils/rest_helper.py,sha256=
|
|
67
|
+
cli/utils/rest_helper.py,sha256=fGfA2PE5AL86xs03Q9xXUyAmgBMfjSQjV1I2n_UQWqs,14090
|
|
68
68
|
cli/utils/time.py,sha256=TEKcNZ-pQoJ7cZ6hQmVD0sTRwRm2rBy51-MuDNdO4S4,296
|
|
69
69
|
cli/utils/versions.py,sha256=mHs_BCk0-wwPfCHIIQBlpFkgipSWkw9jUaZ-USH8ODY,3824
|
|
70
|
-
remotivelabs_cli-0.3.
|
|
71
|
-
remotivelabs_cli-0.3.
|
|
72
|
-
remotivelabs_cli-0.3.
|
|
73
|
-
remotivelabs_cli-0.3.
|
|
74
|
-
remotivelabs_cli-0.3.
|
|
70
|
+
remotivelabs_cli-0.3.5.dist-info/LICENSE,sha256=qDPP_yfuv1fF-u7EfexN-cN3M8aFgGVndGhGLovLKz0,608
|
|
71
|
+
remotivelabs_cli-0.3.5.dist-info/METADATA,sha256=9lhOIqBbAc_vIRppm-Q1W67JW4DcwcyMQUcGs7aRoHQ,1521
|
|
72
|
+
remotivelabs_cli-0.3.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
73
|
+
remotivelabs_cli-0.3.5.dist-info/entry_points.txt,sha256=lvDhPgagLqW_KTnLPCwKSqfYlEp-1uYVosRiPjsVj10,45
|
|
74
|
+
remotivelabs_cli-0.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|