remotivelabs-cli 0.2.3__py3-none-any.whl → 0.3.0__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/remotive.py CHANGED
@@ -9,10 +9,9 @@ from trogon import Trogon
9
9
  from typer.main import get_group
10
10
 
11
11
  from cli.broker.brokers import app as broker_app
12
- from cli.cloud.cloud_cli import app as cloud_app
12
+ from cli.cloud import app as cloud_app
13
13
  from cli.connect.connect import app as connect_app
14
- from cli.settings import settings
15
- from cli.settings.core import Settings
14
+ from cli.settings import Settings, settings
16
15
  from cli.settings.migration.migrate_all_token_files import migrate_any_legacy_tokens
17
16
  from cli.settings.migration.migrate_config_file import migrate_config_file
18
17
  from cli.settings.migration.migrate_legacy_dirs import migrate_legacy_settings_dirs
@@ -82,7 +81,7 @@ def set_default_org_as_env(settings: Settings) -> None:
82
81
  This has to be done early before it is read
83
82
  """
84
83
  if "REMOTIVE_CLOUD_ORGANIZATION" not in os.environ:
85
- active_account = settings.get_cli_config().get_active_account()
84
+ active_account = settings.get_active_account()
86
85
  if active_account and active_account.default_organization:
87
86
  os.environ["REMOTIVE_CLOUD_ORGANIZATION"] = active_account.default_organization
88
87
 
@@ -113,21 +112,15 @@ def tui(ctx: typer.Context) -> None:
113
112
 
114
113
 
115
114
  app.add_typer(broker_app, name="broker", help="Manage a single broker - local or cloud")
116
- app.add_typer(
117
- cloud_app,
118
- name="cloud",
119
- help="Manage resources in RemotiveCloud",
120
- )
115
+ app.add_typer(cloud_app, name="cloud", help="Manage resources in RemotiveCloud")
121
116
  app.add_typer(connect_app, name="connect", help="Integrations with other systems")
122
117
  app.add_typer(tools_app, name="tools")
118
+ app.add_typer(
119
+ topology_app,
120
+ name="topology",
121
+ help="""
122
+ Interact and manage RemotiveTopology resources
123
123
 
124
- if is_featue_flag_enabled("REMOTIVE_TOPOLOGY_ENABLED"):
125
- app.add_typer(
126
- topology_app,
127
- name="topology",
128
- help="""
129
- RemotiveTopology actions
130
-
131
- Read more at https://docs.remotivelabs.com/docs/remotive-topology
132
- """,
133
- )
124
+ Read more at https://docs.remotivelabs.com/docs/remotive-topology
125
+ """,
126
+ )
cli/settings/__init__.py CHANGED
@@ -1,14 +1,13 @@
1
1
  from cli.settings.config_file import Account, ConfigFile
2
2
  from cli.settings.config_file import dumps as dumps_config_file
3
3
  from cli.settings.config_file import loads as loads_config_file
4
- from cli.settings.core import InvalidSettingsFilePathError, Settings, TokenNotFoundError, settings
4
+ from cli.settings.core import InvalidSettingsFilePathError, Settings, settings
5
5
  from cli.settings.token_file import TokenFile
6
6
  from cli.settings.token_file import dumps as dumps_token_file
7
7
  from cli.settings.token_file import loads as loads_token_file
8
8
 
9
9
  __all__ = [
10
10
  "settings",
11
- "TokenNotFoundError",
12
11
  "InvalidSettingsFilePathError",
13
12
  "Settings",
14
13
  "TokenFile",
@@ -10,6 +10,8 @@ from cli.settings.token_file import TokenFile
10
10
  class Account(BaseModel):
11
11
  """
12
12
  Account represents an account in the configuration file.
13
+
14
+ TODO: Add email field to Account
13
15
  """
14
16
 
15
17
  credentials_file: str
cli/settings/core.py CHANGED
@@ -3,18 +3,16 @@ from __future__ import annotations
3
3
  import os
4
4
  import stat
5
5
  import sys
6
- from json import JSONDecodeError
7
6
  from pathlib import Path
8
- from typing import Optional, Tuple
7
+ from typing import Optional
9
8
 
10
- from pydantic import ValidationError
11
9
  from rich.console import Console
12
10
 
13
11
  from cli.errors import ErrorPrinter
14
12
  from cli.settings import config_file as cf
15
13
  from cli.settings import state_file as sf
16
14
  from cli.settings import token_file as tf
17
- from cli.settings.config_file import ConfigFile
15
+ from cli.settings.config_file import Account, ConfigFile
18
16
  from cli.settings.state_file import StateFile
19
17
  from cli.settings.token_file import TokenFile
20
18
 
@@ -24,27 +22,17 @@ CONFIG_DIR_PATH = Path.home() / ".config" / "remotive"
24
22
  CLI_CONFIG_FILE_NAME = "config.json"
25
23
  CLI_INTERNAL_STATE_FILE_NAME = "app-state.json"
26
24
 
27
- TokenFileMetadata = Tuple[TokenFile, Path]
28
-
29
25
 
30
26
  class InvalidSettingsFilePathError(Exception):
31
27
  """Raised when trying to access an invalid settings file or file path"""
32
28
 
33
29
 
34
- class TokenNotFoundError(Exception):
35
- """Raised when a token cannot be found in settings"""
36
-
37
-
38
30
  class Settings:
39
31
  """
40
32
  Settings handles tokens and other config for the remotive CLI
41
33
 
42
- TODO: return None instead of raising errors when no active account is found
43
- TODO: be consisten in how we update (and write) state to the different files
44
34
  TODO: migrate away from singleton instance
45
- TODO: what about manually downloaded tokens when removing a token?
46
- TODO: what about active token when removing a token?
47
- TODO: list tokens should use better listing logic
35
+ TODO: How do we handle REMOTIVE_CLOUD_ACCESS_TOKEN in combination with active account? What takes precedence?
48
36
  """
49
37
 
50
38
  config_dir: Path
@@ -60,17 +48,23 @@ class Settings:
60
48
  if not self.state_file_path.exists():
61
49
  self._write_state_file(StateFile())
62
50
 
63
- def get_cli_config(self) -> ConfigFile:
51
+ def _get_cli_config(self) -> ConfigFile:
64
52
  return self._read_config_file()
65
53
 
66
- def get_state_file(self) -> StateFile:
54
+ def _get_state_file(self) -> StateFile:
67
55
  return self._read_state_file()
68
56
 
57
+ def should_perform_update_check(self) -> bool:
58
+ """
59
+ Check if we should perform an update check.
60
+ """
61
+ return self._get_state_file().should_perform_update_check()
62
+
69
63
  def set_default_organisation(self, organisation: str) -> None:
70
64
  """
71
65
  Set the default organization for the active account
72
66
  """
73
- config = self.get_cli_config()
67
+ config = self._get_cli_config()
74
68
  active_account = config.get_active_account()
75
69
  if not active_account:
76
70
  ErrorPrinter.print_hint("You must have an account activated in order to set default organization")
@@ -78,39 +72,52 @@ class Settings:
78
72
  active_account.default_organization = organisation
79
73
  self._write_config_file(config)
80
74
 
81
- def get_active_token(self) -> str:
75
+ def get_active_account(self) -> Account | None:
82
76
  """
83
- Get the current active token secret
77
+ Get the current active account
78
+
79
+ TODO: Add email field to Account
84
80
  """
85
- token_file = self.get_active_token_file()
86
- return token_file.token
81
+ return self._get_cli_config().get_active_account()
87
82
 
88
- def get_active_token_file(self) -> TokenFile:
83
+ def get_active_token_file(self) -> TokenFile | None:
89
84
  """
90
- Get the current active token file
85
+ Get the token file for the current active account
91
86
  """
92
- active_account = self.get_cli_config().get_active_account()
93
- if not active_account:
94
- raise TokenNotFoundError("No active account found")
87
+ active_account = self.get_active_account()
88
+ return self._read_token_file(active_account.credentials_file) if active_account else None
95
89
 
96
- token_file_name = active_account.credentials_file
97
- return self._read_token_file(self.config_dir / token_file_name)
90
+ def get_active_token(self) -> str | None:
91
+ """
92
+ Get the token secret for the current active account
93
+ """
94
+ token_file = self.get_active_token_file()
95
+ return token_file.token if token_file else None
98
96
 
99
- def activate_token(self, token_file: TokenFile) -> None:
97
+ def activate_token(self, token_file: TokenFile) -> TokenFile:
100
98
  """
101
99
  Activate a token by name or path
102
100
 
103
101
  The token secret will be set as the current active secret.
102
+
103
+ Returns the activated token file
104
104
  """
105
- config = self.get_cli_config()
105
+ config = self._get_cli_config()
106
106
  config.activate_account(token_file.account.email)
107
107
  self._write_config_file(config)
108
+ return token_file
109
+
110
+ def is_active_account(self, email: str) -> bool:
111
+ """
112
+ Returns True if the given email is the active account
113
+ """
114
+ return self._get_cli_config().active == email
108
115
 
109
- def clear_active_token(self) -> None:
116
+ def clear_active_account(self) -> None:
110
117
  """
111
118
  Clear the current active token
112
119
  """
113
- config = self.get_cli_config()
120
+ config = self._get_cli_config()
114
121
  config.active = None
115
122
  self._write_config_file(config)
116
123
 
@@ -120,78 +127,56 @@ class Settings:
120
127
 
121
128
  If multiple tokens are found, the first one is returned.
122
129
  """
123
- tokens = [t for t in self.list_personal_tokens() if t.account is not None and t.account.email == email]
124
- if len(tokens) > 0:
125
- return tokens[0]
126
- tokens = [t for t in self.list_service_account_tokens() if t.account is not None and t.account.email == email]
127
- if len(tokens) > 0:
128
- return tokens[0]
129
- return None
130
+ accounts = self._get_cli_config().accounts.get(email)
131
+ return self._read_token_file(accounts.credentials_file) if accounts else None
130
132
 
131
- def get_token_file(self, name: str) -> TokenFile:
133
+ def get_token_file(self, name: str) -> TokenFile | None:
132
134
  """
133
135
  Get a token file by name or path
134
136
  """
135
137
  # 1. Try relative path
136
138
  if (self.config_dir / name).exists():
137
- return self._read_token_file(self.config_dir / name)
138
-
139
- # 2. Try absolute path
140
- if Path(name).exists():
141
- return self._read_token_file(Path(name))
139
+ return self._read_token_file(name)
142
140
 
143
- # 3. Try name
144
- return self._get_token_by_name(name)[0]
141
+ # 2. Try name
142
+ return self._get_token_by_name(name)
145
143
 
146
144
  def remove_token_file(self, name: str) -> None:
147
145
  """
148
146
  Remove a token file by name or path
149
-
150
- TODO: what about manually downloaded tokens?
151
147
  """
152
- if Path(name).exists():
153
- if self.config_dir not in Path(name).parents:
154
- raise InvalidSettingsFilePathError(f"cannot remove a token file not located in settings dir {self.config_dir}")
155
- return Path(name).unlink()
148
+ token_file = self.get_token_file(name)
149
+ if not token_file:
150
+ return
151
+
152
+ # If the token is active, clear it first
153
+ email = token_file.account.email
154
+ if self.is_active_account(email):
155
+ self.clear_active_account()
156
156
 
157
- # TODO: what about the active token?
158
- path = self._get_token_by_name(name)[1]
159
- return path.unlink()
157
+ # Remove the token file
158
+ path = self.config_dir / self._get_cli_config().accounts[email].credentials_file
159
+ path.unlink()
160
+
161
+ # Remove the account from the config file
162
+ config = self._get_cli_config()
163
+ config.remove_account(email)
164
+ self._write_config_file(config)
160
165
 
161
166
  def add_personal_token(self, token: str, activate: bool = False, overwrite_if_exists: bool = False) -> TokenFile:
162
167
  """
163
168
  Add a personal token
164
169
  """
165
- file = tf.loads(token)
166
- if file.type != "authorized_user":
170
+ token_file = tf.loads(token)
171
+ if token_file.type != "authorized_user":
167
172
  raise ValueError("Token type MUST be authorized_user")
168
173
 
169
- file_name = file.get_token_file_name()
170
- path = self.config_dir / file_name
171
- if path.exists() and not overwrite_if_exists:
172
- raise FileExistsError(f"Token file already exists: {path}")
173
-
174
- self._write_token_file(path, file)
175
- cli_config = self.get_cli_config()
176
- cli_config.init_account(email=file.account.email, token_file=file)
177
- self._write_config_file(cli_config)
174
+ token_file = self.add_token_as_account(token_file, overwrite_if_exists)
178
175
 
179
176
  if activate:
180
- self.activate_token(file)
181
-
182
- return file
177
+ self.activate_token(token_file)
183
178
 
184
- def list_personal_tokens(self) -> list[TokenFile]:
185
- """
186
- List all personal tokens
187
- """
188
- return [f[0] for f in self._list_personal_tokens()]
189
-
190
- def list_personal_token_files(self) -> list[Path]:
191
- """
192
- List paths to all personal token files
193
- """
194
- return [f[1] for f in self._list_personal_tokens()]
179
+ return token_file
195
180
 
196
181
  def add_service_account_token(self, token: str, overwrite_if_exists: bool = False) -> TokenFile:
197
182
  """
@@ -201,29 +186,74 @@ class Settings:
201
186
  if token_file.type != "service_account":
202
187
  raise ValueError("Token type MUST be service_account")
203
188
 
204
- file = token_file.get_token_file_name()
205
- path = self.config_dir / file
189
+ return self.add_token_as_account(token_file, overwrite_if_exists)
190
+
191
+ def add_token_as_account(self, token_file: TokenFile, overwrite_if_exists: bool = False) -> TokenFile:
192
+ """
193
+ Add an account to the config file
194
+ """
195
+ file_name = token_file.get_token_file_name()
196
+ path = self.config_dir / file_name
206
197
  if path.exists() and not overwrite_if_exists:
207
198
  raise FileExistsError(f"Token file already exists: {path}")
208
199
 
209
200
  self._write_token_file(path, token_file)
210
- cli_config = self.get_cli_config()
201
+ cli_config = self._get_cli_config()
211
202
  cli_config.init_account(email=token_file.account.email, token_file=token_file)
212
203
  self._write_config_file(cli_config)
213
204
 
214
205
  return token_file
215
206
 
216
- def list_service_account_tokens(self) -> list[TokenFile]:
207
+ def list_accounts(self) -> dict[str, Account]:
217
208
  """
218
- List all service account tokens
209
+ List all accounts
219
210
  """
220
- return [f[0] for f in self._list_service_account_tokens()]
211
+ return self._get_cli_config().accounts
221
212
 
222
- def list_service_account_token_files(self) -> list[Path]:
213
+ def list_personal_accounts(self) -> dict[str, Account]:
223
214
  """
224
- List paths to all service account token files
215
+ List all personal accounts
216
+
217
+ TODO: add account type to Account
225
218
  """
226
- return [f[1] for f in self._list_service_account_tokens()]
219
+ accounts = self.list_accounts()
220
+ return {
221
+ email: account
222
+ for email, account in accounts.items()
223
+ if self._read_token_file(account.credentials_file).type == "authorized_user"
224
+ }
225
+
226
+ def list_service_accounts(self) -> dict[str, Account]:
227
+ """
228
+ List all personal accounts
229
+
230
+ TODO: add account type to Account
231
+ """
232
+ accounts = self.list_accounts()
233
+ return {
234
+ email: account
235
+ for email, account in accounts.items()
236
+ if self._read_token_file(account.credentials_file).type == "service_account"
237
+ }
238
+
239
+ def list_token_files(self) -> list[TokenFile]:
240
+ """
241
+ List all token files
242
+ """
243
+ accounts = self._get_cli_config().accounts.values()
244
+ return [self._read_token_file(account.credentials_file) for account in accounts]
245
+
246
+ def list_personal_token_files(self) -> list[TokenFile]:
247
+ """
248
+ List all personal token files
249
+ """
250
+ return [token_file for token_file in self.list_token_files() if token_file.type == "authorized_user"]
251
+
252
+ def list_service_account_token_files(self) -> list[TokenFile]:
253
+ """
254
+ List all service account token files
255
+ """
256
+ return [token_file for token_file in self.list_token_files() if token_file.type == "service_account"]
227
257
 
228
258
  def set_last_update_check_time(self, timestamp: str) -> None:
229
259
  """
@@ -233,78 +263,46 @@ class Settings:
233
263
  state.last_update_check_time = timestamp
234
264
  self._write_state_file(state)
235
265
 
236
- def _list_personal_tokens(self) -> list[TokenFileMetadata]:
237
- return self._list_token_files(prefix=tf.PERSONAL_TOKEN_FILE_PREFIX)
238
-
239
- def _list_service_account_tokens(self) -> list[TokenFileMetadata]:
240
- return self._list_token_files(prefix=tf.SERVICE_ACCOUNT_TOKEN_FILE_PREFIX)
241
-
242
- def _get_token_by_name(self, name: str) -> TokenFileMetadata:
243
- token_files = self._list_token_files()
244
- matches = [token_file for token_file in token_files if token_file[0].name == name]
245
- if len(matches) != 1:
246
- raise TokenNotFoundError(f"Ambiguous token file name {name}, found {len(matches)} files")
247
- return matches[0]
248
-
249
- def _list_token_files(self, prefix: str = "") -> list[TokenFileMetadata]:
266
+ def _get_token_by_name(self, name: str) -> TokenFile | None:
250
267
  """
251
- list all tokens with the correct prefix in the config dir, but omit files that are not token files
252
-
253
- TODO: improve is_valid_json and is_valid_token_file using token_file parsing instead
268
+ Token name is only available as a property of TokenFile, so we must iterate over all tokens to find the right one
254
269
  """
270
+ token_files = self.list_token_files()
271
+ matches = [token_file for token_file in token_files if token_file.name == name]
272
+ if len(matches) != 1:
273
+ return None
274
+ return matches[0]
255
275
 
256
- def is_valid_json(path: Path) -> bool:
257
- try:
258
- self._read_token_file(path)
259
- return True
260
- except (JSONDecodeError, ValidationError):
261
- # TODO - this should be printed but printing it here causes it to be displayed to many times
262
- # err_console.print(f"File is not valid json, skipping. {path}")
263
- return False
264
-
265
- def is_valid_token_file(path: Path) -> bool:
266
- is_token_file = path.name.startswith(tf.SERVICE_ACCOUNT_TOKEN_FILE_PREFIX) or path.name.startswith(
267
- tf.PERSONAL_TOKEN_FILE_PREFIX
268
- )
269
- has_correct_prefix = path.is_file() and path.name.startswith(prefix)
270
- is_cli_config = path == self.config_file_path
271
- is_present_in_cli_config_accounts = any(
272
- path.name == account.credentials_file for account in self.get_cli_config().accounts.values()
273
- )
274
- return is_token_file and is_valid_json(path) and has_correct_prefix and not is_cli_config and is_present_in_cli_config_accounts
275
-
276
- paths = [path for path in self.config_dir.iterdir() if is_valid_token_file(path)]
277
- return [(self._read_token_file(token_file), token_file) for token_file in paths]
278
-
279
- def _read_token_file(self, path: Path) -> TokenFile:
276
+ def _read_token_file(self, file_name: str) -> TokenFile:
277
+ path = self.config_dir / file_name
280
278
  data = self._read_file(path)
281
279
  return tf.loads(data)
282
280
 
281
+ def _write_token_file(self, path: Path, token: TokenFile) -> Path:
282
+ data = tf.dumps(token)
283
+ return self._write_file(path, data)
284
+
283
285
  def _read_config_file(self) -> ConfigFile:
284
286
  data = self._read_file(self.config_file_path)
285
287
  return cf.loads(data)
286
288
 
289
+ def _write_config_file(self, config: ConfigFile) -> Path:
290
+ data = cf.dumps(config)
291
+ return self._write_file(self.config_file_path, data)
292
+
287
293
  def _read_state_file(self) -> StateFile:
288
294
  data = self._read_file(self.state_file_path)
289
295
  return sf.loads(data)
290
296
 
297
+ def _write_state_file(self, state: StateFile) -> Path:
298
+ data = sf.dumps(state)
299
+ return self._write_file(self.state_file_path, data)
300
+
291
301
  def _read_file(self, path: Path) -> str:
292
302
  if not path.exists():
293
303
  raise FileNotFoundError(f"File could not be found: {path}")
294
304
  return path.read_text(encoding="utf-8")
295
305
 
296
- def _write_token_file(self, path: Path, token: TokenFile) -> Path:
297
- data = tf.dumps(token)
298
- return self._write_file(path, data)
299
-
300
- def _write_config_file(self, config: ConfigFile) -> Path:
301
- data = cf.dumps(config)
302
- return self._write_file(self.config_file_path, data)
303
-
304
- def _write_state_file(self, state: StateFile) -> Path:
305
- data = sf.dumps(state)
306
- return self._write_file(self.state_file_path, data)
307
-
308
306
  def _write_file(self, path: Path, data: str) -> Path:
309
307
  if self.config_dir not in path.parents:
310
308
  raise InvalidSettingsFilePathError(f"file {path} not in settings dir {self.config_dir}")
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from typing import Any, Optional
7
7
 
8
8
  from cli.settings.config_file import ConfigFile, loads
9
- from cli.settings.core import Settings, TokenNotFoundError
9
+ from cli.settings.core import Settings
10
10
  from cli.settings.migration.migration_tools import get_token_file
11
11
 
12
12
 
@@ -21,13 +21,20 @@ def migrate_account_data(config: dict[str, Any], settings: Settings) -> Optional
21
21
  cred_name = account_info.pop("credentials_name", None)
22
22
  if not cred_name:
23
23
  continue
24
+
25
+ # found legacy account, try to migrate it, or drop it...
24
26
  found_old = True
25
- try:
26
- cred_file = get_token_file(cred_name, settings.config_dir).get_token_file_name()
27
- except TokenNotFoundError:
28
- # schedule this account for removal
27
+
28
+ token_file = get_token_file(cred_name, settings.config_dir)
29
+ if not token_file:
30
+ sys.stderr.write(f"Dropping account {account_email!r}: credentials file for {cred_name} not found")
31
+ to_delete.append(account_email)
32
+ continue
33
+
34
+ cred_file = token_file.get_token_file_name()
35
+ if not cred_file:
36
+ sys.stderr.write(f"Dropping account {account_email!r}: credentials file for {cred_name} not found")
29
37
  to_delete.append(account_email)
30
- sys.stderr.write(f"Dropping account {account_email!r}: token file for {cred_name} not found")
31
38
  continue
32
39
 
33
40
  account_info["credentials_file"] = cred_file
@@ -1,7 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  from itertools import chain
2
4
  from pathlib import Path
3
5
 
4
- from cli.settings.core import TokenNotFoundError
5
6
  from cli.settings.token_file import TokenFile
6
7
 
7
8
 
@@ -23,7 +24,7 @@ def list_token_files(config_dir: Path) -> list[TokenFile]:
23
24
  return token_files
24
25
 
25
26
 
26
- def get_token_file(cred_name: str, config_dir: Path) -> TokenFile:
27
+ def get_token_file(cred_name: str, config_dir: Path) -> TokenFile | None:
27
28
  """
28
29
  Get the token file for a given credentials name.
29
30
 
@@ -32,5 +33,5 @@ def get_token_file(cred_name: str, config_dir: Path) -> TokenFile:
32
33
  token_files = list_token_files(config_dir)
33
34
  matches = [token_file for token_file in token_files if token_file.name == cred_name]
34
35
  if len(matches) != 1:
35
- raise TokenNotFoundError(f"Token file for {cred_name} not found")
36
+ return None
36
37
  return matches[0]
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from datetime import datetime
3
+ import os
4
+ from datetime import datetime, timedelta
4
5
  from typing import Any, Optional
5
6
 
6
7
  from pydantic import BaseModel
@@ -10,9 +11,7 @@ from cli.utils.time import parse_datetime
10
11
 
11
12
  class StateFile(BaseModel):
12
13
  """
13
- StateFile represents the state file for the CLI.
14
-
15
- TODO: Should all setters return a new instance of the StateFile?
14
+ Contains CLI state and other application specific data.
16
15
  """
17
16
 
18
17
  version: str = "1.0"
@@ -23,8 +22,17 @@ class StateFile(BaseModel):
23
22
  Check if we should perform an update check.
24
23
 
25
24
  Returns True if the last update check time is older than 2 hours.
25
+
26
+ For Docker environments, returns False and sets a backdated timestamp
27
+ to prevent constant update checks due to ephemeral disks.
26
28
  """
27
29
  if not self.last_update_check_time:
30
+ if os.environ.get("RUNS_IN_DOCKER"):
31
+ # To prevent that we always check update in docker due to ephemeral disks we write an "old" check if the state
32
+ # is missing. If no disk is mounted we will never get the update check but if its mounted properly we will get
33
+ # it on the second attempt. This is good enough
34
+ self.last_update_check_time = (datetime.now() - timedelta(hours=10)).isoformat()
35
+ return False
28
36
  return True # Returning True will trigger a check, which will properly set last_update_check_time
29
37
 
30
38
  seconds = (datetime.now() - parse_datetime(self.last_update_check_time)).seconds
cli/topology/cmd.py CHANGED
@@ -8,7 +8,7 @@ import typer
8
8
  from rich.console import Console
9
9
 
10
10
  from cli.errors import ErrorPrinter
11
- from cli.settings import TokenNotFoundError, settings
11
+ from cli.settings import settings
12
12
  from cli.typer import typer_utils
13
13
  from cli.utils.rest_helper import RestHelper
14
14
 
@@ -64,9 +64,8 @@ def start_trial(
64
64
 
65
65
  """
66
66
  RestHelper.use_progress("Checking access tokens...", transient=True)
67
- try:
68
- _ = settings.get_active_token_file()
69
- except TokenNotFoundError:
67
+ active_token = settings.get_active_token_file()
68
+ if not active_token:
70
69
  if len(settings.list_personal_token_files()) == 0:
71
70
  console.print(
72
71
  "You must first sign in to RemotiveCloud, please use [bold]remotive cloud auth login[/bold] to sign-in"
@@ -74,7 +73,7 @@ def start_trial(
74
73
  )
75
74
  else:
76
75
  console.print(
77
- "You have not active account, please run [bold]remotive cloud auth activate[/bold] to choose an account"
76
+ "You have not actived your account, please run [bold]remotive cloud auth activate[/bold] to choose an account"
78
77
  "or [bold]remotive cloud auth login[/bold] to sign-in"
79
78
  )
80
79
  return
@@ -84,7 +83,7 @@ def start_trial(
84
83
  ErrorPrinter.print_generic_message("Your current active credentials are not valid")
85
84
  raise typer.Exit(1)
86
85
 
87
- active_account = settings.get_cli_config().get_active_account()
86
+ active_account = settings.get_active_account()
88
87
  if active_account and not organization and not active_account.default_organization:
89
88
  ErrorPrinter.print_hint("You have not specified any organization and no default organization is set")
90
89
  raise typer.Exit(1)