manta-node 0.5b0.dev77__py3-none-any.whl → 0.5b0.dev82__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.
@@ -63,6 +63,13 @@ class TaskLifecycleService:
63
63
  }
64
64
  return status_map.get(container_status, TaskStatus.FAILED)
65
65
 
66
+ @staticmethod
67
+ def status_from_exit(container_status: str, exit_code: int) -> TaskStatus:
68
+ """For image-mode tasks: exit code is the authoritative completion signal."""
69
+ if container_status == "exited":
70
+ return TaskStatus.COMPLETED if exit_code == 0 else TaskStatus.FAILED
71
+ return TaskLifecycleService.calculate_status_from_container(container_status)
72
+
66
73
  @staticmethod
67
74
  def should_restart_task(
68
75
  current_iteration: int,
@@ -1,9 +1,11 @@
1
1
  """Container infrastructure - Docker runtime management."""
2
2
 
3
3
  from .docker_adapter import DockerRaiser
4
+ from .image_executor import ImageExecutor
4
5
  from .manager import ContainerManager
5
6
 
6
7
  __all__ = [
7
8
  "ContainerManager",
8
9
  "DockerRaiser",
10
+ "ImageExecutor",
9
11
  ]
@@ -0,0 +1,86 @@
1
+ from datetime import datetime
2
+ from typing import Dict, Optional, Tuple
3
+
4
+ from aiodocker.docker import DockerContainer
5
+
6
+ from manta_common.build.common.tasks import TaskStatus
7
+ from manta_common.conversions import ID
8
+ from manta_common.errors import MantaDockerError
9
+ from manta_common.traces import Tracer
10
+
11
+ from ...domain.task_lifecycle import TaskLifecycleService
12
+ from .docker_adapter import DockerRaiser
13
+
14
+
15
+ class ImageExecutor(DockerRaiser):
16
+ """
17
+ Runs plain Docker images with native ENTRYPOINT/CMD.
18
+ No zipapp, no manta SDK, no gRPC inside the container.
19
+ """
20
+
21
+ tracer: Tracer
22
+ containers: Dict[Tuple[ID, ID], DockerContainer]
23
+
24
+ async def run_image(
25
+ self,
26
+ swarm_id: ID,
27
+ task_id: ID,
28
+ image: str,
29
+ environment: Optional[dict] = None,
30
+ ) -> datetime:
31
+ """
32
+ Pull (if not present locally) and start a Docker image with its native ENTRYPOINT.
33
+
34
+ Returns
35
+ -------
36
+ datetime
37
+ Container start time
38
+ """
39
+ try:
40
+ await self.async_client.images.inspect(image)
41
+ except Exception:
42
+ self.tracer.info(f"Image {image} not found locally, pulling...")
43
+ await self.pull_image_or_raise(image)
44
+
45
+ env = [f"{k}={v}" for k, v in environment.items()] if environment else None
46
+ config = {
47
+ "Image": image,
48
+ "HostConfig": {
49
+ "NetworkMode": "host",
50
+ "ExtraHosts": ["host.docker.internal:host-gateway"],
51
+ },
52
+ "Env": env,
53
+ "Detach": True,
54
+ "LogConfig": {
55
+ "type": "json-file",
56
+ "config": {"max-size": "10m", "max-file": "3"},
57
+ },
58
+ "Labels": {
59
+ "com.docker.compose.project": "manta_tasks",
60
+ },
61
+ }
62
+
63
+ start_time = datetime.now()
64
+ self.tracer.debug(f"Starting image {image} for task {task_id}")
65
+ self.containers[(swarm_id, task_id)] = await self.run_or_raise(config)
66
+ self.tracer.info(f"Task {task_id} image container started.")
67
+ return start_time
68
+
69
+ async def get_image_task_status(self, swarm_id: ID, task_id: ID) -> TaskStatus:
70
+ """
71
+ Exit-code-aware status for image-mode tasks (no SDK inside the container).
72
+ """
73
+ container = self.containers.get((swarm_id, task_id))
74
+ if container is None:
75
+ raise MantaDockerError("Container not found", metadata=self.tracer.metadata)
76
+
77
+ inspect = await container.show()
78
+ container_status = inspect["State"]["Status"]
79
+ exit_code = inspect["State"].get("ExitCode", 0)
80
+
81
+ status = TaskLifecycleService.status_from_exit(container_status, exit_code)
82
+ self.tracer.debug(
83
+ f"Task {task_id} image status: {container_status} "
84
+ f"(exit={exit_code}) → {status}"
85
+ )
86
+ return status
@@ -3,11 +3,11 @@ from collections import deque
3
3
  from datetime import datetime
4
4
  from pathlib import Path
5
5
  from tempfile import TemporaryDirectory
6
- from typing import List, Optional, Tuple
6
+ from typing import List, Optional, Set, Tuple
7
7
 
8
8
  from manta_common.build.common.system import EnvId, PairId
9
9
  from manta_common.build.common.tasks import MqttTask, TaskStatus, TaskUpdate
10
- from manta_common.conversions import ID
10
+ from manta_common.conversions import ID, TupleID
11
11
  from manta_common.errors import (
12
12
  MantaError,
13
13
  MantaGRPCError,
@@ -16,6 +16,7 @@ from manta_common.errors import (
16
16
  )
17
17
  from manta_common.traces import Tracer
18
18
  from .domain.task_lifecycle import TaskLifecycleService
19
+ from .infrastructure.container.image_executor import ImageExecutor
19
20
  from .infrastructure.container.manager import ContainerManager
20
21
  from .infrastructure.grpc.client import NodeClient
21
22
  from .tasks import TaskProgression, TaskProgressions
@@ -23,7 +24,7 @@ from .tasks import TaskProgression, TaskProgressions
23
24
  __all__ = ["TaskManager"]
24
25
 
25
26
 
26
- class TaskManager(ContainerManager):
27
+ class TaskManager(ContainerManager, ImageExecutor):
27
28
  """
28
29
  The Task Manager is responsible to distribute tasks depending on device resources.
29
30
  It is composed with a Queue of Tasks.
@@ -54,6 +55,7 @@ class TaskManager(ContainerManager):
54
55
  # Initialize the task queue
55
56
  self.task_queue = deque()
56
57
  self.future_task = None
58
+ self.image_task_ids: Set[Tuple[TupleID, TupleID]] = set()
57
59
 
58
60
  ContainerManager.__init__(self)
59
61
 
@@ -155,6 +157,11 @@ class TaskManager(ContainerManager):
155
157
  updated.append(task_progression)
156
158
  return updated
157
159
 
160
+ async def get_task_status(self, swarm_id: ID, task_id: ID) -> TaskStatus:
161
+ if (swarm_id.tid, task_id.tid) in self.image_task_ids:
162
+ return await self.get_image_task_status(swarm_id, task_id)
163
+ return await ContainerManager.get_task_status(self, swarm_id, task_id)
164
+
158
165
  async def from_pending_to_running(self, task_progression: TaskProgression) -> bool:
159
166
  """
160
167
  Change a task progression from pending status (supposed) to running status
@@ -219,56 +226,69 @@ class TaskManager(ContainerManager):
219
226
  )
220
227
  self.tracer.debug(f"Creating task {task_id} for {swarm_id}")
221
228
 
222
- # Create a temporary file and write the module code
223
- # (even if the file does not exist, it will be created)
224
- module_file = self.module_folder / f"{swarm_id.oid}_{task_id.oid}.pyz"
225
- module_file.unlink(missing_ok=True)
226
- module_file.touch(exist_ok=True)
227
- module_file.write_bytes(task.payload)
228
-
229
- container_file = "/usr/src/module.pyz"
230
-
231
- network = None
232
- if task.network != str(None):
233
- if task.auth_key != str(None) or task.login_server != str(None):
234
- self.tracer.debug(
235
- f"Task {task_id} has auth_key {task.auth_key} and login_server {task.login_server}"
236
- )
237
- network = await self.ensure_tailscale_service(
238
- swarm_id,
239
- task.auth_key,
240
- task.login_server,
241
- task.alias,
242
- self.module_folder,
243
- )
244
- else:
245
- self.tracer.error(
246
- f"Task {task_id} has network {task.network} but no auth_key or login_server"
247
- )
248
- raise MantaNodeError(
249
- f"Task {task_id} has network {task.network} but no auth_key or login_server",
250
- metadata=self.tracer.metadata,
251
- )
252
-
253
- # Deploy the task
254
- start_time = await self.run_container(
255
- swarm_id=swarm_id,
256
- task_id=task_id,
257
- image=task.image,
258
- command=f"python {container_file}",
259
- volumes={module_file.absolute(): {"bind": container_file, "mode": "ro"}},
260
- environment={
261
- "TASK_ID": task_id.xid,
262
- "SWARM_ID": swarm_id.xid,
263
- "RPC_PORT": self.ls_port,
264
- "RPC_HOST": "host.docker.internal",
265
- },
266
- gpu=task.gpu,
267
- network=network,
268
- alias=task.alias,
269
- )
229
+ if not task.payload:
230
+ # Image execution path: native ENTRYPOINT, no zipapp, no gRPC inside
231
+ module_file = self.module_folder / f"{swarm_id.oid}_{task_id.oid}.img"
232
+ start_time = await self.run_image(
233
+ swarm_id=swarm_id,
234
+ task_id=task_id,
235
+ image=task.image,
236
+ environment={
237
+ "TASK_ID": task_id.xid,
238
+ "SWARM_ID": swarm_id.xid,
239
+ },
240
+ )
241
+ self.image_task_ids.add((swarm_id.tid, task_id.tid))
242
+ else:
243
+ # Zipapp execution path (existing)
244
+ module_file = self.module_folder / f"{swarm_id.oid}_{task_id.oid}.pyz"
245
+ module_file.unlink(missing_ok=True)
246
+ module_file.touch(exist_ok=True)
247
+ module_file.write_bytes(task.payload)
248
+
249
+ container_file = "/usr/src/module.pyz"
250
+
251
+ network = None
252
+ if task.network != str(None):
253
+ if task.auth_key != str(None) or task.login_server != str(None):
254
+ self.tracer.debug(
255
+ f"Task {task_id} has auth_key {task.auth_key} and login_server {task.login_server}"
256
+ )
257
+ network = await self.ensure_tailscale_service(
258
+ swarm_id,
259
+ task.auth_key,
260
+ task.login_server,
261
+ task.alias,
262
+ self.module_folder,
263
+ )
264
+ else:
265
+ self.tracer.error(
266
+ f"Task {task_id} has network {task.network} but no auth_key or login_server"
267
+ )
268
+ raise MantaNodeError(
269
+ f"Task {task_id} has network {task.network} but no auth_key or login_server",
270
+ metadata=self.tracer.metadata,
271
+ )
272
+
273
+ start_time = await self.run_container(
274
+ swarm_id=swarm_id,
275
+ task_id=task_id,
276
+ image=task.image,
277
+ command=f"python {container_file}",
278
+ volumes={
279
+ module_file.absolute(): {"bind": container_file, "mode": "ro"}
280
+ },
281
+ environment={
282
+ "TASK_ID": task_id.xid,
283
+ "SWARM_ID": swarm_id.xid,
284
+ "RPC_PORT": self.ls_port,
285
+ "RPC_HOST": "host.docker.internal",
286
+ },
287
+ gpu=task.gpu,
288
+ network=network,
289
+ alias=task.alias,
290
+ )
270
291
 
271
- # Update the task information
272
292
  self.tasks.append(
273
293
  TaskProgression(
274
294
  task_update=TaskUpdate(
@@ -511,9 +531,6 @@ class TaskManager(ContainerManager):
511
531
  task_progression.swarm_id,
512
532
  task_progression.task_id,
513
533
  )
514
- self.tasks.pop(
515
- (
516
- task_progression.swarm_id.tid,
517
- task_progression.task_id.tid,
518
- )
519
- )
534
+ env_id = (task_progression.swarm_id.tid, task_progression.task_id.tid)
535
+ self.tasks.pop(env_id)
536
+ self.image_task_ids.discard(env_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: manta_node
3
- Version: 0.5b0.dev77
3
+ Version: 0.5b0.dev82
4
4
  Summary: Node side software of the Manta platform. It is responsible for the communication with the Manta Core and the execution of the tasks.
5
5
  Author-email: Hugo Miralles <hugomiralles@manta-tech.io>, Matthew Thompson <matthew.thompson@manta-tech.io>
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -1,7 +1,7 @@
1
1
  manta_node/__init__.py,sha256=bgGWXH3yQVhNf0v1ZGUBAy5Y51fqb0Y-EaLnocbcino,153
2
2
  manta_node/__main__.py,sha256=0VzbY1KfezaARZDuCBc7Th1GO_-BnKTK8eDEXhgP5J4,1731
3
3
  manta_node/node_orchestrator.py,sha256=0VMBsZSMQiIe9L-Bd7Jn4avNpNyDCMa1kyPSTgRiOes,17336
4
- manta_node/task_manager.py,sha256=LFFE83JreNa9YejFa8JTRFkZgVOjFREqdYfQREYD2S4,17406
4
+ manta_node/task_manager.py,sha256=8Yy8ShoKbMemvm3UMPt9xJnoZsxiCptqdWnhAKJnhl4,18498
5
5
  manta_node/tasks.py,sha256=W__XjU17AX8VqOtgy8HRBmxWtVufTkFvri8s8Y3R394,6721
6
6
  manta_node/utils.py,sha256=gkYRB7fylk1jy1sm7nKU7o8eadGxn0AX4IjCHT6Wzuw,1179
7
7
  manta_node/cli/__init__.py,sha256=arzO3mXDJ-E2yeeytbZfN7mCswwxdQ43_QjyeL2E5yE,134
@@ -16,12 +16,13 @@ manta_node/cli/commands/start.py,sha256=49kvB6p01nBTp0yqLX36RiXeEccGYTcl9YMcB2sI
16
16
  manta_node/cli/commands/status.py,sha256=9YYUaelmQJWqh6FbVuy6y2y1lvjoPIkA2d18JU0PHls,6430
17
17
  manta_node/cli/commands/stop.py,sha256=mcO8AMtaPkpNFEy6lVIe4fvNFJSEkheFdxkj1ySiYaI,7719
18
18
  manta_node/domain/__init__.py,sha256=uyJXDf0QgfGCsXLMggqKCB5QV9oneYJ4iM-REaJGXDM,217
19
- manta_node/domain/task_lifecycle.py,sha256=RRRXYvOraID0mg5TrInbQLeyUGzSeU6HIGUvvo6rbTw,2856
19
+ manta_node/domain/task_lifecycle.py,sha256=bnmarFdgrRoGkDeDlDj6OSOoCT8-qg00OMrny30stNI,3248
20
20
  manta_node/infrastructure/__init__.py,sha256=yM60jKhdaD18BOsLb-0aR9D2XxGvrTnV92oOT7Kor2w,297
21
21
  manta_node/infrastructure/config/__init__.py,sha256=jbRzb1c0RohpF83WOwtDpe19dBXVfbZYF3SoOaESh6k,617
22
22
  manta_node/infrastructure/config/node_config_manager.py,sha256=iAzMWPjs2AWTxwDLAdCK8GpMMw_14QOzX11hvQMnSvY,30414
23
- manta_node/infrastructure/container/__init__.py,sha256=9I9g5y2ncEAa7PhEsSfL2nFOltfFr-GsBGgn8B2vIUY,199
23
+ manta_node/infrastructure/container/__init__.py,sha256=EO4SndUwmXrHczM4qkleUbxC7lTGz3c3Hd3iGrPg6D8,262
24
24
  manta_node/infrastructure/container/docker_adapter.py,sha256=Sxprl0RpjT8epxTzYUJxSyHko3IBcus2ihneq4kRH7Q,6980
25
+ manta_node/infrastructure/container/image_executor.py,sha256=PfLCqzpn8HVsxpPEpZmoQYnFQj4W91jrZUjcntnyzoQ,2881
25
26
  manta_node/infrastructure/container/manager.py,sha256=qpHwBOsDdiv10i5Qlx9PUn5UHr1oy8ejRGalTCi6AOQ,17375
26
27
  manta_node/infrastructure/filesystem/__init__.py,sha256=nf6Fakp3wuFKjSAm_oihI6WE_yyUY6Uelr7A1GwSvXs,145
27
28
  manta_node/infrastructure/filesystem/dataset_manager.py,sha256=lCcsLdDHM-rRfhndpD9fQ56rPKYcuel-p3qx8nUxffE,14894
@@ -34,9 +35,9 @@ manta_node/infrastructure/metrics/collector.py,sha256=nhFPAbjA04ye3UaGN95_zdpf9h
34
35
  manta_node/infrastructure/metrics/metrics_collector.py,sha256=jhEocAPWS6xgbNvcBBiEB9cAN_9hEGssZJXoysQzWOk,10518
35
36
  manta_node/infrastructure/mqtt/__init__.py,sha256=riXo1sXgO4YV6DHCmWRqK61BAmuGLdHiVdQFunAJadA,140
36
37
  manta_node/infrastructure/mqtt/command_handler.py,sha256=bDH8k3Vkbbb7q3P68VamhMbtLJ4vSSivqZP3IfMBqIQ,11833
37
- manta_node-0.5b0.dev77.dist-info/licenses/LICENSE,sha256=P5HojupjKoAusNtHqWOvO-YFolwLFWQ7Uq0XVKK9nFQ,35291
38
- manta_node-0.5b0.dev77.dist-info/METADATA,sha256=fYHO13_SNuUvlRtsYIxxU4mogXC-Xnly4XjpydG0wRQ,44653
39
- manta_node-0.5b0.dev77.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
40
- manta_node-0.5b0.dev77.dist-info/entry_points.txt,sha256=C_zcw6mdmztNxVUZGLZ83UH5TwcSuqb2C-rB4e2CQoc,56
41
- manta_node-0.5b0.dev77.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
42
- manta_node-0.5b0.dev77.dist-info/RECORD,,
38
+ manta_node-0.5b0.dev82.dist-info/licenses/LICENSE,sha256=P5HojupjKoAusNtHqWOvO-YFolwLFWQ7Uq0XVKK9nFQ,35291
39
+ manta_node-0.5b0.dev82.dist-info/METADATA,sha256=R5IOzQVXU_UwtIuwH0c9gwyBZoR3uyLUOHspdhs4Xms,44653
40
+ manta_node-0.5b0.dev82.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
41
+ manta_node-0.5b0.dev82.dist-info/entry_points.txt,sha256=C_zcw6mdmztNxVUZGLZ83UH5TwcSuqb2C-rB4e2CQoc,56
42
+ manta_node-0.5b0.dev82.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
43
+ manta_node-0.5b0.dev82.dist-info/RECORD,,