primitive 0.2.25__py3-none-any.whl → 0.2.27__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.
primitive/__about__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Dylan Stein <dylan@primitive.tech>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.2.25"
4
+ __version__ = "0.2.27"
@@ -24,11 +24,11 @@ class Agent(BaseAction):
24
24
  diagnose=True,
25
25
  level="DEBUG" if self.primitive.DEBUG else "INFO",
26
26
  )
27
- logger.info("[*] primitive agent")
28
- logger.info(f"[*] Version: {__version__}")
27
+ logger.info("primitive agent")
28
+ logger.info(f"Version: {__version__}")
29
29
 
30
- # Initialize the database
31
- sqlite.init()
30
+ # Wait for monitor to make database
31
+ sqlite.wait_for_db()
32
32
 
33
33
  # Create uploader
34
34
  uploader = Uploader(primitive=self.primitive)
@@ -107,4 +107,4 @@ class Agent(BaseAction):
107
107
 
108
108
  sleep(5)
109
109
  except KeyboardInterrupt:
110
- logger.info("[*] Stopping primitive agent...")
110
+ logger.info("[agent] Stopping primitive agent...")
primitive/db/sqlite.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from pathlib import Path
2
+ from time import sleep
2
3
 
3
4
  from loguru import logger
4
5
  from sqlalchemy import Engine, create_engine
@@ -13,12 +14,12 @@ def init() -> None:
13
14
 
14
15
  # Drop DB existing database if it exists
15
16
  # if db_path.exists():
16
- # logger.warning(f"[*] Deleting existing SQLite database at {db_path}")
17
+ # logger.warning(f"Deleting existing SQLite database at {db_path}")
17
18
  # db_path.unlink()
18
19
  if db_path.exists():
19
20
  return
20
21
 
21
- logger.info(f"[*] Initializing SQLite database at {db_path}")
22
+ logger.info(f"Initializing SQLite database at {db_path}")
22
23
  engine = create_engine(f"sqlite:///{db_path}", echo=False)
23
24
  Base.metadata.create_all(engine)
24
25
 
@@ -28,6 +29,28 @@ def engine() -> Engine:
28
29
  return create_engine(f"sqlite:///{db_path}", echo=False)
29
30
 
30
31
 
32
+ def wait_for_db() -> None:
33
+ # Wait for the database to be created
34
+ db_path: Path = get_cache_dir() / "primitive.sqlite3"
35
+
36
+ max_tries = 60
37
+ current_try = 1
38
+ while not db_path.exists() and current_try <= max_tries:
39
+ logger.debug(
40
+ f"Waiting for SQLite database to be created... [{current_try} / {max_tries}]"
41
+ )
42
+ db_path.touch()
43
+ sleep(1)
44
+ current_try += 1
45
+ continue
46
+ if current_try > max_tries:
47
+ logger.error(
48
+ f"SQLite database was not created after {max_tries} tries. Exiting..."
49
+ )
50
+ raise RuntimeError("SQLite database was not created.")
51
+ logger.debug("SQLite database is ready.")
52
+
53
+
31
54
  def Session() -> SQLAlchemySession:
32
55
  from sqlalchemy.orm import sessionmaker
33
56
 
@@ -366,11 +366,11 @@ class Hardware(BaseAction):
366
366
  mutation, variable_values=variables, get_execution_result=True
367
367
  )
368
368
  except client_exceptions.ClientConnectorError as exception:
369
- message = "[*] Failed to update hardware system info! "
369
+ message = "Failed to update hardware system info! "
370
370
  logger.exception(message)
371
371
  raise exception
372
372
 
373
- message = "[*] Updated hardware system info successfully! "
373
+ message = "Updated hardware system info successfully! "
374
374
  logger.info(message)
375
375
 
376
376
  return result
@@ -432,7 +432,7 @@ class Hardware(BaseAction):
432
432
  }
433
433
  self.status_cache[fingerprint] = new_state.copy()
434
434
 
435
- message = f"[*] Checked in successfully for {fingerprint}: "
435
+ message = f"Checked in successfully for {fingerprint}: "
436
436
  is_new_status = False
437
437
  for key, value in new_state.items():
438
438
  if value != previous_status.get(key, None):
@@ -460,7 +460,7 @@ class Hardware(BaseAction):
460
460
  return result
461
461
  except client_exceptions.ClientConnectorError as exception:
462
462
  if not stopping_agent:
463
- message = "[*] Failed to check in! "
463
+ message = "Failed to check in! "
464
464
  logger.error(message)
465
465
  raise exception
466
466
  else:
@@ -1,5 +1,6 @@
1
1
  import sys
2
2
  from time import sleep
3
+ from typing import Optional
3
4
 
4
5
  import psutil
5
6
  from loguru import logger
@@ -12,7 +13,7 @@ from primitive.utils.exceptions import P_CLI_100
12
13
 
13
14
 
14
15
  class Monitor(BaseAction):
15
- def start(self):
16
+ def start(self, job_run_id: Optional[str] = None):
16
17
  logger.remove()
17
18
  logger.add(
18
19
  sink=sys.stderr,
@@ -21,24 +22,36 @@ class Monitor(BaseAction):
21
22
  diagnose=True,
22
23
  level="DEBUG" if self.primitive.DEBUG else "INFO",
23
24
  )
24
- logger.info("[*] primitive monitor")
25
- logger.info(f"[*] Version: {__version__}")
25
+ logger.info("primitive monitor")
26
+ logger.info(f"Version: {__version__}")
26
27
 
27
- try:
28
- # hey stupid:
29
- # do not set is_available to True here, it will mess up the reservation logic
30
- # only set is_available after we've checked that no active reservation is present
31
- # setting is_available of the parent also effects the children,
32
- # which may have active reservations as well
33
- self.primitive.hardware.check_in_http(is_online=True)
34
- except Exception as exception:
35
- logger.exception(f"Error checking in hardware: {exception}")
36
- sys.exit(1)
28
+ # TODO: tighten logic for determining if we're running in a container
29
+ RUNNING_IN_CONTAINER = False
30
+ if job_run_id is not None:
31
+ logger.info("Running in container...")
32
+ RUNNING_IN_CONTAINER = True
33
+
34
+ # can't check if if it is a container
35
+ if not RUNNING_IN_CONTAINER:
36
+ try:
37
+ # hey stupid:
38
+ # do not set is_available to True here, it will mess up the reservation logic
39
+ # only set is_available after we've checked that no active reservation is present
40
+ # setting is_available of the parent also effects the children,
41
+ # which may have active reservations as well
42
+ self.primitive.hardware.check_in_http(is_online=True)
43
+ except Exception as exception:
44
+ logger.exception(f"Error checking in hardware: {exception}")
45
+ sys.exit(1)
37
46
 
38
47
  # Initialize the database
39
48
  sqlite.init()
40
49
 
41
50
  try:
51
+ if job_run_id is not None:
52
+ JobRun.objects.create(job_run_id=job_run_id, pid=None)
53
+ logger.debug(f"Creating job run in database: {job_run_id}")
54
+
42
55
  active_reservation_id = None
43
56
  active_reservation_pk = None
44
57
 
@@ -95,6 +108,13 @@ class Monitor(BaseAction):
95
108
  sleep(1)
96
109
  continue
97
110
 
111
+ if RUNNING_IN_CONTAINER:
112
+ # if we get here and we're running in a container,
113
+ # it means the job run is complete and there is nothing left in the database
114
+ # so we can exit
115
+ logger.debug("Running in container, initial job complete.")
116
+ sys.exit(0)
117
+
98
118
  # Second, check for active reservations
99
119
  hardware = self.primitive.hardware.get_own_hardware_details()
100
120
  if hardware["activeReservation"]:
@@ -187,13 +207,14 @@ class Monitor(BaseAction):
187
207
  logger.debug(f"Creating job run in database: {job_run['id']}")
188
208
 
189
209
  except KeyboardInterrupt:
190
- logger.info("[*] Stopping primitive monitor...")
210
+ logger.info("Stopping primitive monitor...")
191
211
  try:
192
- self.primitive.hardware.check_in_http(
193
- is_available=False, is_online=False, stopping_agent=True
194
- )
212
+ if not RUNNING_IN_CONTAINER:
213
+ self.primitive.hardware.check_in_http(
214
+ is_available=False, is_online=False, stopping_agent=True
215
+ )
195
216
 
196
217
  except P_CLI_100 as exception:
197
- logger.error("[*] Error stopping primitive monitor.")
218
+ logger.error("Error stopping primitive monitor.")
198
219
  logger.error(str(exception))
199
220
  sys.exit()
@@ -1,13 +1,15 @@
1
+ from typing import TYPE_CHECKING, Optional
2
+
1
3
  import click
2
- import typing
3
4
 
4
- if typing.TYPE_CHECKING:
5
+ if TYPE_CHECKING:
5
6
  from ..client import Primitive
6
7
 
7
8
 
8
9
  @click.command("monitor")
10
+ @click.option("--job-run-id", type=str, help="Explicit Job Run to pull")
9
11
  @click.pass_context
10
- def cli(context):
12
+ def cli(context, job_run_id: Optional[str] = None):
11
13
  """monitor"""
12
14
  primitive: Primitive = context.obj.get("PRIMITIVE")
13
- primitive.monitor.start()
15
+ primitive.monitor.start(job_run_id=job_run_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: primitive
3
- Version: 0.2.25
3
+ Version: 0.2.27
4
4
  Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
5
5
  Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
6
6
  Project-URL: Source, https://github.com//primitivecorp/primitive-cli
@@ -1,9 +1,9 @@
1
- primitive/__about__.py,sha256=emMCm4cHzvmdYDOL83iUzQz3eDl0L-1JkrP95MpoyT8,130
1
+ primitive/__about__.py,sha256=DiY29pKV2K5D1JKr9jAi4jEELMktClhq9SkZurDA7L0,130
2
2
  primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
3
3
  primitive/cli.py,sha256=g7EtHI9MATAB0qQu5w-WzbXtxz_8zu8z5E7sETmMkKU,2509
4
4
  primitive/client.py,sha256=h8WZVnQylVe0vbpuyC8YZHl2JyITSPC-1HbUcmrE5pc,3623
5
5
  primitive/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- primitive/agent/actions.py,sha256=PzFOgxuRrhbUGpUygnTZVru58Fv87GI8tgJYkZB1LjI,3773
6
+ primitive/agent/actions.py,sha256=bTzEaMOs5kK8cnJw6VvrGeJNrXmu8rr_-2-87k8uNkg,3786
7
7
  primitive/agent/commands.py,sha256=cK7d3OcN5Z65gQWVZFQ-Y9ddw9Pes4f9OVBpeMsj5sE,255
8
8
  primitive/agent/runner.py,sha256=CoRyReO3jPV8B7vILVWdszFD4GVop7HsVEUo1hoRXjo,14556
9
9
  primitive/agent/uploader.py,sha256=ZzrzsajNBogwEC7mT6Ejy0h2Jd9axMYGzt9pbCvVMlk,3171
@@ -20,7 +20,7 @@ primitive/daemons/launch_service.py,sha256=iuklHeuEqadlf8U1n9xFg4ZG1EKdK2jyaPI-V
20
20
  primitive/daemons/ui.py,sha256=Af3OJWJ0jdGlb1nfA5yaGYdhBEqqpM8zP2U2vUQdCbw,1236
21
21
  primitive/db/base.py,sha256=mH7f2d_jiyxJSSx9Gk53QBXRa3LiKBsBjkFgvmtH1WA,83
22
22
  primitive/db/models.py,sha256=GfnJdAq4Tb68CI4BKAuJDZVqioGavveaAHbCPeLNngw,2840
23
- primitive/db/sqlite.py,sha256=3V9ZxbgME1ThfJp90MPLUxU8b9imgNZM5CHOnA-WkaQ,953
23
+ primitive/db/sqlite.py,sha256=chX4ih_TCn1Y-jJn8WGOC2prqBUIpRUJrUwQ2KaYsko,1659
24
24
  primitive/exec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  primitive/exec/actions.py,sha256=4d_TCjNDcVFoZ9Zw7ZuBa6hKMv2Xzm7_UX_8wcX1aSk,4124
26
26
  primitive/exec/commands.py,sha256=66LO2kkJC-ynNZQpUCXv4Ol15QoacdSZAHblePDcmLo,510
@@ -42,7 +42,7 @@ primitive/graphql/relay.py,sha256=bmij2AjdpURQ6GGVCxwWhauF-r_SxuAU2oJ4sDbLxpI,72
42
42
  primitive/graphql/sdk.py,sha256=KhVWDZms_eMBgt6ftSJitRALguagy-nmrj4IC2taeXY,1535
43
43
  primitive/graphql/utility_fragments.py,sha256=uIjwILC4QtWNyO5vu77VjQf_p0jvP3A9q_6zRq91zqs,303
44
44
  primitive/hardware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- primitive/hardware/actions.py,sha256=d5KwuSsceOhDH9rgOL7YTCpQPhqT2inRTiZnROtiDic,26076
45
+ primitive/hardware/actions.py,sha256=qBMcCcgSf3XRe-ua1unf5M64J-Y8EelUQKysa8vje0s,26060
46
46
  primitive/hardware/android.py,sha256=tu7pBPxWFrIwb_mm5CEdFFf1_veNDOKjOCQg13i_Lh4,2758
47
47
  primitive/hardware/commands.py,sha256=NMliVHBZDl4UAvhmNEjrvN9KWPuqn87-d7eVb0ZqEYA,3752
48
48
  primitive/hardware/ui.py,sha256=12rucuZ2s-w5R4bKyxON5dEbrdDnVf5sbj3K_nbdo44,2473
@@ -57,8 +57,8 @@ primitive/jobs/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
57
57
  primitive/jobs/graphql/fragments.py,sha256=1_ZttT7dx36KDC3DClJz9M8LMpsPwXySBygHSiUEcGg,619
58
58
  primitive/jobs/graphql/mutations.py,sha256=8ASvCmwQh7cMeeiykOdYaYVryG8FRIuVF6v_J8JJZuw,219
59
59
  primitive/jobs/graphql/queries.py,sha256=BrU_GnLjK0bTAmWsLSmGEUea7EM8MqTKxN1Qp6sSjwc,1597
60
- primitive/monitor/actions.py,sha256=GUQrwuan82pOJ5gI2FvQYzgDoP4fs28PdcI_fg_aXRs,8692
61
- primitive/monitor/commands.py,sha256=dZsD8WKGU4OYO_AlKawfeRNVTMN0xJ-DFRkmKTS464s,258
60
+ primitive/monitor/actions.py,sha256=kgXzj45B-mFZke06pOHdKJ4RTDo32_YRV5hMpS65Pjk,9706
61
+ primitive/monitor/commands.py,sha256=VDlEL_Qpm_ysHxug7VpI0cVAZ0ny6AS91Y58D7F1zkU,409
62
62
  primitive/organizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
63
  primitive/organizations/actions.py,sha256=kVHOhG1oS2sI5p8uldSo5L-RUZsnG36eaulVuKLyZ-M,1863
64
64
  primitive/organizations/commands.py,sha256=_dwgVEJCqMa5VgB_7P1wLPFc0AuT1p9dtyR9JRr4kpw,487
@@ -96,8 +96,8 @@ primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIU
96
96
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
97
97
  primitive/utils/shell.py,sha256=jWzb7ky7p987dJas6ZvarK3IJNZ5cwBXcryRWb9Uh6U,2072
98
98
  primitive/utils/text.py,sha256=XiESMnlhjQ534xE2hMNf08WehE1SKaYFRNih0MmnK0k,829
99
- primitive-0.2.25.dist-info/METADATA,sha256=KzAEz2pPIFN5lBO7_7jERQ7oIL1trv05Jjqdp8icC9w,3569
100
- primitive-0.2.25.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
101
- primitive-0.2.25.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
102
- primitive-0.2.25.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
103
- primitive-0.2.25.dist-info/RECORD,,
99
+ primitive-0.2.27.dist-info/METADATA,sha256=CaxlEEfLCa1XxJ6ydce8TLWbPShjNVzfrJgUxHn6PXo,3569
100
+ primitive-0.2.27.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
101
+ primitive-0.2.27.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
102
+ primitive-0.2.27.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
103
+ primitive-0.2.27.dist-info/RECORD,,