manta-node 0.6b0.dev607__py3-none-any.whl → 0.6b0.dev615__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.
- manta_node/infrastructure/container/__init__.py +2 -1
- manta_node/infrastructure/container/docker_adapter.py +103 -15
- manta_node/infrastructure/container/image_executor.py +1 -3
- manta_node/infrastructure/container/manager.py +2 -3
- {manta_node-0.6b0.dev607.dist-info → manta_node-0.6b0.dev615.dist-info}/METADATA +1 -1
- {manta_node-0.6b0.dev607.dist-info → manta_node-0.6b0.dev615.dist-info}/RECORD +10 -10
- {manta_node-0.6b0.dev607.dist-info → manta_node-0.6b0.dev615.dist-info}/WHEEL +0 -0
- {manta_node-0.6b0.dev607.dist-info → manta_node-0.6b0.dev615.dist-info}/entry_points.txt +0 -0
- {manta_node-0.6b0.dev607.dist-info → manta_node-0.6b0.dev615.dist-info}/licenses/LICENSE +0 -0
- {manta_node-0.6b0.dev607.dist-info → manta_node-0.6b0.dev615.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Container infrastructure - Docker runtime management."""
|
|
2
2
|
|
|
3
|
-
from .docker_adapter import DockerRaiser
|
|
3
|
+
from .docker_adapter import DockerRaiser, ImageFreshness
|
|
4
4
|
from .image_executor import ImageExecutor
|
|
5
5
|
from .manager import ContainerManager
|
|
6
6
|
|
|
@@ -8,4 +8,5 @@ __all__ = [
|
|
|
8
8
|
"ContainerManager",
|
|
9
9
|
"DockerRaiser",
|
|
10
10
|
"ImageExecutor",
|
|
11
|
+
"ImageFreshness",
|
|
11
12
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import concurrent.futures
|
|
3
3
|
from datetime import datetime
|
|
4
|
+
from enum import Enum
|
|
4
5
|
|
|
5
6
|
from aiodocker import Docker
|
|
6
7
|
from aiodocker.docker import DockerContainer
|
|
@@ -10,6 +11,30 @@ from manta_common.errors import MantaDockerError
|
|
|
10
11
|
from manta_common.traces import Tracer
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
class ImageFreshness(Enum):
|
|
15
|
+
"""How the local copy of an image compares with the registry's.
|
|
16
|
+
|
|
17
|
+
THREE outcomes, and ``UNKNOWN`` must never be collapsed into ``CURRENT``.
|
|
18
|
+
A caller that cannot tell *these are the registry's bytes* from *nobody
|
|
19
|
+
could ask* runs stale bytes while reporting an ordinary start -- silent in
|
|
20
|
+
both directions, which is the defect in #457.
|
|
21
|
+
|
|
22
|
+
What separates ``UNKNOWN`` from ``CURRENT`` is WHY no comparison happened,
|
|
23
|
+
never that none happened. An image with no ``RepoDigests`` was built
|
|
24
|
+
locally and never pushed: there is nothing it could be stale against, so it
|
|
25
|
+
is ``CURRENT``. That is not a degraded reading and must not become one --
|
|
26
|
+
``install_manta_platform.sh`` builds ``manta-light:pytorch`` under exactly
|
|
27
|
+
that unprefixed name, and only the name distinguishes it from the registry
|
|
28
|
+
image, so reporting it ``UNKNOWN`` would put a permanent warning on a
|
|
29
|
+
correct state and spend the third outcome's credibility on the case where
|
|
30
|
+
it means nothing.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
CURRENT = "current"
|
|
34
|
+
STALE = "stale"
|
|
35
|
+
UNKNOWN = "unknown"
|
|
36
|
+
|
|
37
|
+
|
|
13
38
|
class DockerRaiser:
|
|
14
39
|
async_client: Docker
|
|
15
40
|
tracer: Tracer
|
|
@@ -21,9 +46,9 @@ class DockerRaiser:
|
|
|
21
46
|
max_workers=4, thread_name_prefix="docker-pull"
|
|
22
47
|
)
|
|
23
48
|
|
|
24
|
-
async def
|
|
49
|
+
async def image_freshness(self, image: str) -> ImageFreshness:
|
|
25
50
|
"""
|
|
26
|
-
|
|
51
|
+
Compare the local copy of ``image`` with the registry's.
|
|
27
52
|
|
|
28
53
|
Compares the local image's ``RepoDigests`` against the remote manifest
|
|
29
54
|
digest, obtained via the Docker daemon's own distribution API
|
|
@@ -39,21 +64,23 @@ class DockerRaiser:
|
|
|
39
64
|
|
|
40
65
|
Returns
|
|
41
66
|
-------
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
ImageFreshness
|
|
68
|
+
``STALE`` if the image must be (re)pulled, ``CURRENT`` if the local
|
|
69
|
+
bytes are the right ones, ``UNKNOWN`` if that question could not be
|
|
70
|
+
answered.
|
|
44
71
|
|
|
45
72
|
Notes
|
|
46
73
|
-----
|
|
47
|
-
|
|
48
|
-
unauthenticated, rate-limited, ...)
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
Never raises out of task launch. A registry that cannot be reached
|
|
75
|
+
(unreachable, unauthenticated, rate-limited, ...) yields ``UNKNOWN``
|
|
76
|
+
and a warning, NOT ``CURRENT``: the caller decides what to do about an
|
|
77
|
+
unanswered question, and it must be able to see that it was unanswered.
|
|
51
78
|
"""
|
|
52
79
|
try:
|
|
53
80
|
local = await self.async_client.images.inspect(image)
|
|
54
81
|
except Exception:
|
|
55
82
|
# Image not present locally: must pull regardless of the registry.
|
|
56
|
-
return
|
|
83
|
+
return ImageFreshness.STALE
|
|
57
84
|
|
|
58
85
|
local_digests = {
|
|
59
86
|
digest.split("@", 1)[1]
|
|
@@ -63,9 +90,9 @@ class DockerRaiser:
|
|
|
63
90
|
if not local_digests:
|
|
64
91
|
# Locally-built/tagged image (never pushed/pulled), or only
|
|
65
92
|
# malformed RepoDigests entries: nothing to compare against the
|
|
66
|
-
# registry.
|
|
67
|
-
#
|
|
68
|
-
return
|
|
93
|
+
# registry. CURRENT, not UNKNOWN — there is no question left open
|
|
94
|
+
# here, so the registry is never consulted and no warning is due.
|
|
95
|
+
return ImageFreshness.CURRENT
|
|
69
96
|
|
|
70
97
|
try:
|
|
71
98
|
# Default executor, NOT _PULL_EXECUTOR: this quick probe must not
|
|
@@ -75,13 +102,74 @@ class DockerRaiser:
|
|
|
75
102
|
)
|
|
76
103
|
remote_digest = distribution["Descriptor"]["digest"]
|
|
77
104
|
except Exception as exc:
|
|
105
|
+
# Wording pinned by test_image_freshness_keeps_the_operator_grep_target.
|
|
78
106
|
self.tracer.warning(
|
|
79
107
|
f"Cannot verify registry digest for {image}, skipping "
|
|
80
108
|
f"auto-pull staleness check: {exc!r}"
|
|
81
109
|
)
|
|
82
|
-
return
|
|
110
|
+
return ImageFreshness.UNKNOWN
|
|
83
111
|
|
|
84
|
-
|
|
112
|
+
if remote_digest in local_digests:
|
|
113
|
+
return ImageFreshness.CURRENT
|
|
114
|
+
return ImageFreshness.STALE
|
|
115
|
+
|
|
116
|
+
async def pull_if_stale(
|
|
117
|
+
self, image: str, force_pull: bool = False
|
|
118
|
+
) -> ImageFreshness:
|
|
119
|
+
"""
|
|
120
|
+
The single site that decides whether a container start pulls first.
|
|
121
|
+
|
|
122
|
+
BOTH container-start paths call this — ``ContainerManager.run_container``
|
|
123
|
+
(zipapp) and ``ImageExecutor.run_image`` (native entrypoint). That is the
|
|
124
|
+
invariant, and it is the whole point: a start path that decides for
|
|
125
|
+
itself can silently stop deciding, because nothing about one path's
|
|
126
|
+
behaviour is asserted by the other's tests (#457, #306).
|
|
127
|
+
|
|
128
|
+
``STALE`` and ``UNKNOWN`` are deliberately NOT treated alike. Known-wrong
|
|
129
|
+
bytes are refused loudly: the pull runs and ``pull_image_or_raise``
|
|
130
|
+
propagates its failure, so the launch dies rather than running an image
|
|
131
|
+
we have established is out of date. An unanswered question proceeds on
|
|
132
|
+
the local copy and says so, because refusing there would turn any
|
|
133
|
+
registry outage into a fleet-wide stoppage — and ``image_freshness``
|
|
134
|
+
commits to never raising out of task launch.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
image : str
|
|
139
|
+
Image reference the container will run
|
|
140
|
+
force_pull : bool
|
|
141
|
+
Pull unconditionally, skipping the comparison entirely — including
|
|
142
|
+
the registry round trip that comparison costs
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
ImageFreshness
|
|
147
|
+
What is known about the bytes the container will run: ``CURRENT``
|
|
148
|
+
when they are the registry's (compared equal, just pulled, or a
|
|
149
|
+
local-only image with nothing to compare), ``UNKNOWN`` when the
|
|
150
|
+
registry could not be asked. ``STALE`` is never returned — a stale
|
|
151
|
+
image is pulled, and a pull that fails raises instead of returning.
|
|
152
|
+
"""
|
|
153
|
+
if force_pull:
|
|
154
|
+
# Ahead of the comparison, not merged into it: the comparison is a
|
|
155
|
+
# registry round trip, and evaluating it here would make the
|
|
156
|
+
# documented bypass the slowest path in this function.
|
|
157
|
+
self.tracer.info(f"Pulling image {image}...")
|
|
158
|
+
await self.pull_image_or_raise(image)
|
|
159
|
+
return ImageFreshness.CURRENT
|
|
160
|
+
|
|
161
|
+
freshness = await self.image_freshness(image)
|
|
162
|
+
if freshness is ImageFreshness.STALE:
|
|
163
|
+
self.tracer.info(f"Pulling image {image}...")
|
|
164
|
+
await self.pull_image_or_raise(image)
|
|
165
|
+
return ImageFreshness.CURRENT
|
|
166
|
+
if freshness is ImageFreshness.UNKNOWN:
|
|
167
|
+
self.tracer.warning(
|
|
168
|
+
f"Starting {image} from the local copy with its freshness "
|
|
169
|
+
f"unverified: the registry digest could not be read, so these "
|
|
170
|
+
f"bytes are unconfirmed rather than known-current."
|
|
171
|
+
)
|
|
172
|
+
return freshness
|
|
85
173
|
|
|
86
174
|
async def run_or_raise(self, config: dict) -> DockerContainer:
|
|
87
175
|
"""
|
|
@@ -147,7 +235,7 @@ class DockerRaiser:
|
|
|
147
235
|
Pulls via the sync docker-py client rather than the async aiodocker
|
|
148
236
|
client. docker-py auto-resolves registry credentials from
|
|
149
237
|
``~/.docker/config.json`` (including credHelpers/credsStore) — the
|
|
150
|
-
same client ``
|
|
238
|
+
same client ``image_freshness`` already uses for
|
|
151
239
|
``inspect_distribution`` — whereas aiodocker pulls anonymously and
|
|
152
240
|
404s on private images with "pull access denied ... may require
|
|
153
241
|
'docker login'" (refs #348).
|
|
@@ -166,9 +166,7 @@ class ImageExecutor(DockerRaiser):
|
|
|
166
166
|
metadata=self.tracer.metadata,
|
|
167
167
|
)
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
self.tracer.info(f"Pulling image {image}...")
|
|
171
|
-
await self.pull_image_or_raise(image)
|
|
169
|
+
await self.pull_if_stale(image, force_pull=force_pull)
|
|
172
170
|
|
|
173
171
|
# Copy (not alias) gpu_config's HostConfig — it is shared, long-lived
|
|
174
172
|
# state on self; mutating it in place would leak NetworkMode/ExtraHosts
|
|
@@ -239,9 +239,8 @@ class ContainerManager(DockerRaiser):
|
|
|
239
239
|
for key, value in config.items():
|
|
240
240
|
self.tracer.debug(f" {key}: {value}")
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
await self.pull_image_or_raise(image)
|
|
242
|
+
# Must resolve the image before the container is created, never after.
|
|
243
|
+
await self.pull_if_stale(image, force_pull=force_pull)
|
|
245
244
|
|
|
246
245
|
self.containers[(swarm_id, task_id)] = await self.run_or_raise(config)
|
|
247
246
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: manta_node
|
|
3
|
-
Version: 0.6b0.
|
|
3
|
+
Version: 0.6b0.dev615
|
|
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 <hugo.miralles@manta-tech.io>, Matthew Thompson <matthew.thompson@manta-tech.io>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -24,11 +24,11 @@ manta_node/infrastructure/__init__.py,sha256=yM60jKhdaD18BOsLb-0aR9D2XxGvrTnV92o
|
|
|
24
24
|
manta_node/infrastructure/executor.py,sha256=wbH5-G4Y3iNTPr8ROWRDT-bWD-CYl1YFlZQ8dd9tpW4,2636
|
|
25
25
|
manta_node/infrastructure/config/__init__.py,sha256=VnKidPAVfzlZMb5WM-MggtsDLM8tdAFFWlB7WSiawzc,617
|
|
26
26
|
manta_node/infrastructure/config/node_config_manager.py,sha256=Js6PISjR_JRXMhgFC7lzI6StjW5paFhdhmtteao9j5A,35228
|
|
27
|
-
manta_node/infrastructure/container/__init__.py,sha256=
|
|
28
|
-
manta_node/infrastructure/container/docker_adapter.py,sha256=
|
|
27
|
+
manta_node/infrastructure/container/__init__.py,sha256=ACDyzGvtM7nfTsBXDAYFjkKgU2yu4Umj3CQ_rlRfw1I,300
|
|
28
|
+
manta_node/infrastructure/container/docker_adapter.py,sha256=Uxd3cNZMPyu7p68fq5QWvkF_vju2SjEBCqsvcnWYI-8,16368
|
|
29
29
|
manta_node/infrastructure/container/executor.py,sha256=0aTFXDv-oXz81qwGi6zl_4qtkFriPWzn7iiiTRg7KJM,7274
|
|
30
|
-
manta_node/infrastructure/container/image_executor.py,sha256=
|
|
31
|
-
manta_node/infrastructure/container/manager.py,sha256=
|
|
30
|
+
manta_node/infrastructure/container/image_executor.py,sha256=noNwde03hw9k3EnK1JuQGtByUf-XDHqx623X0DKgHZ0,10315
|
|
31
|
+
manta_node/infrastructure/container/manager.py,sha256=kylDl-5VZk01tzSXVrWI9VPn8mqi6tV-7Rf1tkMgee0,15790
|
|
32
32
|
manta_node/infrastructure/filesystem/__init__.py,sha256=nf6Fakp3wuFKjSAm_oihI6WE_yyUY6Uelr7A1GwSvXs,145
|
|
33
33
|
manta_node/infrastructure/filesystem/dataset_manager.py,sha256=sW7vPKYBLjwbtju1yzFEob9GlY1v0VI-xu6gxF9KM90,15075
|
|
34
34
|
manta_node/infrastructure/grpc/__init__.py,sha256=6I-yzQM42gdBfcZ1FPVEKF2-pgJMdW-UssDHgz2EFcc,254
|
|
@@ -50,9 +50,9 @@ manta_node/infrastructure/process/executor.py,sha256=Ab1XvNuelEGCD5KdZLgYYnpjYZ5
|
|
|
50
50
|
manta_node/infrastructure/security/__init__.py,sha256=lI6vkbUmfmt0m363iO4HzuzVNoQAc7Clp2R5vAhZbgE,64
|
|
51
51
|
manta_node/infrastructure/security/auth_agent.py,sha256=WkPTg_g0gix7xomb_on_Ft3TNX0FpeqS7gwgJW9mTvk,11037
|
|
52
52
|
manta_node/infrastructure/security/token_provider.py,sha256=u3bcbQYNBskFzunO-wOSZpGkDN_CU1BCB5HUbZTC4Vg,1832
|
|
53
|
-
manta_node-0.6b0.
|
|
54
|
-
manta_node-0.6b0.
|
|
55
|
-
manta_node-0.6b0.
|
|
56
|
-
manta_node-0.6b0.
|
|
57
|
-
manta_node-0.6b0.
|
|
58
|
-
manta_node-0.6b0.
|
|
53
|
+
manta_node-0.6b0.dev615.dist-info/licenses/LICENSE,sha256=9mBgLhiB9goQPQjg9XQJ6FjgVxYw9Q8lyOZYkPGwF_c,34484
|
|
54
|
+
manta_node-0.6b0.dev615.dist-info/METADATA,sha256=5GbkeVrFKSatf7YuxFnn-QqLT2KEE3htAxlFv5jx0Hc,4213
|
|
55
|
+
manta_node-0.6b0.dev615.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
56
|
+
manta_node-0.6b0.dev615.dist-info/entry_points.txt,sha256=C_zcw6mdmztNxVUZGLZ83UH5TwcSuqb2C-rB4e2CQoc,56
|
|
57
|
+
manta_node-0.6b0.dev615.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
|
|
58
|
+
manta_node-0.6b0.dev615.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|