dagster-docker 0.17.19__py3-none-any.whl → 0.28.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.
- dagster_docker/__init__.py +10 -6
- dagster_docker/container_context.py +9 -8
- dagster_docker/docker_executor.py +58 -33
- dagster_docker/docker_run_launcher.py +74 -41
- dagster_docker/ops/__init__.py +1 -1
- dagster_docker/ops/docker_container_op.py +19 -18
- dagster_docker/pipes.py +208 -0
- dagster_docker/py.typed +1 -0
- dagster_docker/utils.py +2 -6
- dagster_docker/version.py +1 -1
- dagster_docker-0.28.5.dist-info/METADATA +32 -0
- dagster_docker-0.28.5.dist-info/RECORD +15 -0
- {dagster_docker-0.17.19.dist-info → dagster_docker-0.28.5.dist-info}/WHEEL +1 -1
- {dagster_docker-0.17.19.dist-info → dagster_docker-0.28.5.dist-info/licenses}/LICENSE +1 -1
- dagster_docker-0.17.19.dist-info/METADATA +0 -22
- dagster_docker-0.17.19.dist-info/RECORD +0 -13
- {dagster_docker-0.17.19.dist-info → dagster_docker-0.28.5.dist-info}/top_level.txt +0 -0
dagster_docker/__init__.py
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
from
|
|
1
|
+
from dagster_shared.libraries import DagsterLibraryRegistry
|
|
2
2
|
|
|
3
|
-
from .docker_executor import docker_executor as docker_executor
|
|
4
|
-
from .docker_run_launcher import DockerRunLauncher as DockerRunLauncher
|
|
5
|
-
from .ops import (
|
|
3
|
+
from dagster_docker.docker_executor import docker_executor as docker_executor
|
|
4
|
+
from dagster_docker.docker_run_launcher import DockerRunLauncher as DockerRunLauncher
|
|
5
|
+
from dagster_docker.ops import (
|
|
6
6
|
docker_container_op as docker_container_op,
|
|
7
7
|
execute_docker_container as execute_docker_container,
|
|
8
8
|
)
|
|
9
|
-
from .
|
|
9
|
+
from dagster_docker.pipes import (
|
|
10
|
+
PipesDockerClient as PipesDockerClient,
|
|
11
|
+
PipesDockerLogsMessageReader as PipesDockerLogsMessageReader,
|
|
12
|
+
)
|
|
13
|
+
from dagster_docker.version import __version__
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
DagsterLibraryRegistry.register("dagster-docker", __version__)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
from typing import TYPE_CHECKING, Any, NamedTuple, Optional, cast
|
|
2
3
|
|
|
3
4
|
from dagster import (
|
|
4
5
|
Array,
|
|
@@ -10,10 +11,10 @@ from dagster import (
|
|
|
10
11
|
from dagster._config import process_config
|
|
11
12
|
from dagster._core.container_context import process_shared_container_context_config
|
|
12
13
|
from dagster._core.errors import DagsterInvalidConfigError
|
|
13
|
-
from dagster._core.storage.
|
|
14
|
+
from dagster._core.storage.dagster_run import DagsterRun
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING:
|
|
16
|
-
from
|
|
17
|
+
from dagster_docker import DockerRunLauncher
|
|
17
18
|
|
|
18
19
|
DOCKER_CONTAINER_CONTEXT_SCHEMA = {
|
|
19
20
|
"registry": Field(
|
|
@@ -79,7 +80,7 @@ class DockerContainerContext(
|
|
|
79
80
|
networks: Optional[Sequence[str]] = None,
|
|
80
81
|
container_kwargs: Optional[Mapping[str, Any]] = None,
|
|
81
82
|
):
|
|
82
|
-
return super(
|
|
83
|
+
return super().__new__(
|
|
83
84
|
cls,
|
|
84
85
|
registry=check.opt_nullable_mapping_param(registry, "registry"),
|
|
85
86
|
env_vars=check.opt_sequence_param(env_vars, "env_vars", of_type=str),
|
|
@@ -102,7 +103,7 @@ class DockerContainerContext(
|
|
|
102
103
|
)
|
|
103
104
|
|
|
104
105
|
@staticmethod
|
|
105
|
-
def create_for_run(
|
|
106
|
+
def create_for_run(dagster_run: DagsterRun, run_launcher: Optional["DockerRunLauncher"]):
|
|
106
107
|
context = DockerContainerContext()
|
|
107
108
|
|
|
108
109
|
# First apply the instance / run_launcher-level context
|
|
@@ -117,8 +118,8 @@ class DockerContainerContext(
|
|
|
117
118
|
)
|
|
118
119
|
|
|
119
120
|
run_container_context = (
|
|
120
|
-
|
|
121
|
-
if
|
|
121
|
+
dagster_run.job_code_origin.repository_origin.container_context
|
|
122
|
+
if dagster_run.job_code_origin
|
|
122
123
|
else None
|
|
123
124
|
)
|
|
124
125
|
|
|
@@ -154,7 +155,7 @@ class DockerContainerContext(
|
|
|
154
155
|
run_docker_container_context,
|
|
155
156
|
)
|
|
156
157
|
|
|
157
|
-
processed_context_value = cast(Mapping[str, Any], processed_container_context.value)
|
|
158
|
+
processed_context_value = cast("Mapping[str, Any]", processed_container_context.value)
|
|
158
159
|
|
|
159
160
|
return shared_container_context.merge(
|
|
160
161
|
DockerContainerContext(
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Iterator
|
|
2
|
+
from typing import TYPE_CHECKING, Optional, cast
|
|
2
3
|
|
|
3
4
|
import dagster._check as check
|
|
4
5
|
import docker
|
|
5
6
|
import docker.errors
|
|
6
7
|
from dagster import Field, IntSource, executor
|
|
7
|
-
from dagster._annotations import
|
|
8
|
+
from dagster._annotations import beta
|
|
8
9
|
from dagster._core.definitions.executor_definition import multiple_process_executor_requirements
|
|
9
|
-
from dagster._core.events import DagsterEvent, EngineEventData
|
|
10
|
+
from dagster._core.events import DagsterEvent, EngineEventData
|
|
10
11
|
from dagster._core.execution.retries import RetryMode, get_retries_config
|
|
12
|
+
from dagster._core.execution.step_dependency_config import (
|
|
13
|
+
StepDependencyConfig,
|
|
14
|
+
get_step_dependency_config_field,
|
|
15
|
+
)
|
|
11
16
|
from dagster._core.execution.tags import get_tag_concurrency_limits_config
|
|
12
17
|
from dagster._core.executor.base import Executor
|
|
13
18
|
from dagster._core.executor.init import InitExecutorContext
|
|
@@ -17,15 +22,15 @@ from dagster._core.executor.step_delegating.step_handler.base import (
|
|
|
17
22
|
StepHandler,
|
|
18
23
|
StepHandlerContext,
|
|
19
24
|
)
|
|
20
|
-
from dagster._core.origin import PipelinePythonOrigin
|
|
21
25
|
from dagster._core.utils import parse_env_var
|
|
22
|
-
from dagster._grpc.types import ExecuteStepArgs
|
|
23
|
-
from dagster._serdes.utils import hash_str
|
|
24
26
|
from dagster._utils.merger import merge_dicts
|
|
27
|
+
from dagster_shared.serdes.utils import hash_str
|
|
25
28
|
|
|
29
|
+
from dagster_docker.container_context import DockerContainerContext
|
|
26
30
|
from dagster_docker.utils import DOCKER_CONFIG_SCHEMA, validate_docker_config, validate_docker_image
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from dagster._core.origin import JobPythonOrigin
|
|
29
34
|
|
|
30
35
|
|
|
31
36
|
@executor(
|
|
@@ -43,14 +48,14 @@ from .container_context import DockerContainerContext
|
|
|
43
48
|
),
|
|
44
49
|
),
|
|
45
50
|
"tag_concurrency_limits": get_tag_concurrency_limits_config(),
|
|
51
|
+
"step_dependency_config": get_step_dependency_config_field(),
|
|
46
52
|
},
|
|
47
53
|
),
|
|
48
54
|
requirements=multiple_process_executor_requirements(),
|
|
49
55
|
)
|
|
50
|
-
@
|
|
56
|
+
@beta
|
|
51
57
|
def docker_executor(init_context: InitExecutorContext) -> Executor:
|
|
52
|
-
"""
|
|
53
|
-
Executor which launches steps as Docker containers.
|
|
58
|
+
"""Executor which launches steps as Docker containers.
|
|
54
59
|
|
|
55
60
|
To use the `docker_executor`, set it as the `executor_def` when defining a job:
|
|
56
61
|
|
|
@@ -101,6 +106,9 @@ def docker_executor(init_context: InitExecutorContext) -> Executor:
|
|
|
101
106
|
retries=check.not_none(RetryMode.from_config(retries)),
|
|
102
107
|
max_concurrent=max_concurrent,
|
|
103
108
|
tag_concurrency_limits=tag_concurrency_limits,
|
|
109
|
+
step_dependency_config=StepDependencyConfig.from_config(
|
|
110
|
+
config.get("step_dependency_config") # type: ignore
|
|
111
|
+
),
|
|
104
112
|
)
|
|
105
113
|
|
|
106
114
|
|
|
@@ -118,10 +126,10 @@ class DockerStepHandler(StepHandler):
|
|
|
118
126
|
)
|
|
119
127
|
|
|
120
128
|
def _get_image(self, step_handler_context: StepHandlerContext):
|
|
121
|
-
from
|
|
129
|
+
from dagster_docker import DockerRunLauncher
|
|
122
130
|
|
|
123
131
|
image = cast(
|
|
124
|
-
|
|
132
|
+
"JobPythonOrigin", step_handler_context.dagster_run.job_code_origin
|
|
125
133
|
).repository_origin.container_image
|
|
126
134
|
if not image:
|
|
127
135
|
image = self._image
|
|
@@ -138,13 +146,13 @@ class DockerStepHandler(StepHandler):
|
|
|
138
146
|
|
|
139
147
|
def _get_docker_container_context(self, step_handler_context: StepHandlerContext):
|
|
140
148
|
# This doesn't vary per step: would be good to have a hook where it can be set once
|
|
141
|
-
# for the whole StepHandler but we need access to the
|
|
149
|
+
# for the whole StepHandler but we need access to the DagsterRun for that
|
|
142
150
|
|
|
143
|
-
from .docker_run_launcher import DockerRunLauncher
|
|
151
|
+
from dagster_docker.docker_run_launcher import DockerRunLauncher
|
|
144
152
|
|
|
145
153
|
run_launcher = step_handler_context.instance.run_launcher
|
|
146
154
|
run_target = DockerContainerContext.create_for_run(
|
|
147
|
-
step_handler_context.
|
|
155
|
+
step_handler_context.dagster_run,
|
|
148
156
|
run_launcher if isinstance(run_launcher, DockerRunLauncher) else None,
|
|
149
157
|
)
|
|
150
158
|
|
|
@@ -172,11 +180,17 @@ class DockerStepHandler(StepHandler):
|
|
|
172
180
|
)
|
|
173
181
|
return client
|
|
174
182
|
|
|
175
|
-
def
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
def _get_step_key(self, step_handler_context: StepHandlerContext) -> str:
|
|
184
|
+
step_keys_to_execute = cast(
|
|
185
|
+
"list[str]", step_handler_context.execute_step_args.step_keys_to_execute
|
|
186
|
+
)
|
|
178
187
|
assert len(step_keys_to_execute) == 1, "Launching multiple steps is not currently supported"
|
|
179
|
-
|
|
188
|
+
return step_keys_to_execute[0]
|
|
189
|
+
|
|
190
|
+
def _get_container_name(self, step_handler_context: StepHandlerContext):
|
|
191
|
+
execute_step_args = step_handler_context.execute_step_args
|
|
192
|
+
run_id = execute_step_args.run_id
|
|
193
|
+
step_key = self._get_step_key(step_handler_context)
|
|
180
194
|
|
|
181
195
|
step_name = f"dagster-step-{hash_str(run_id + step_key)}"
|
|
182
196
|
|
|
@@ -200,17 +214,20 @@ class DockerStepHandler(StepHandler):
|
|
|
200
214
|
assert len(step_keys_to_execute) == 1, "Launching multiple steps is not currently supported"
|
|
201
215
|
step_key = step_keys_to_execute[0]
|
|
202
216
|
|
|
217
|
+
container_kwargs = {**container_context.container_kwargs}
|
|
218
|
+
container_kwargs.pop("stop_timeout", None)
|
|
219
|
+
|
|
203
220
|
env_vars = dict([parse_env_var(env_var) for env_var in container_context.env_vars])
|
|
204
|
-
env_vars["DAGSTER_RUN_JOB_NAME"] = step_handler_context.
|
|
221
|
+
env_vars["DAGSTER_RUN_JOB_NAME"] = step_handler_context.dagster_run.job_name
|
|
205
222
|
env_vars["DAGSTER_RUN_STEP_KEY"] = step_key
|
|
206
223
|
return client.containers.create(
|
|
207
224
|
step_image,
|
|
208
|
-
name=self._get_container_name(
|
|
225
|
+
name=self._get_container_name(step_handler_context),
|
|
209
226
|
detach=True,
|
|
210
227
|
network=container_context.networks[0] if len(container_context.networks) else None,
|
|
211
228
|
command=execute_step_args.get_command_args(),
|
|
212
229
|
environment=env_vars,
|
|
213
|
-
**
|
|
230
|
+
**container_kwargs,
|
|
214
231
|
)
|
|
215
232
|
|
|
216
233
|
def launch_step(self, step_handler_context: StepHandlerContext) -> Iterator[DagsterEvent]:
|
|
@@ -245,9 +262,9 @@ class DockerStepHandler(StepHandler):
|
|
|
245
262
|
yield DagsterEvent.step_worker_starting(
|
|
246
263
|
step_handler_context.get_step_context(step_key),
|
|
247
264
|
message="Launching step in Docker container.",
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
265
|
+
metadata={
|
|
266
|
+
"Docker container id": step_container.id,
|
|
267
|
+
},
|
|
251
268
|
)
|
|
252
269
|
step_container.start()
|
|
253
270
|
|
|
@@ -256,9 +273,15 @@ class DockerStepHandler(StepHandler):
|
|
|
256
273
|
|
|
257
274
|
client = self._get_client(container_context)
|
|
258
275
|
|
|
259
|
-
container_name = self._get_container_name(step_handler_context
|
|
276
|
+
container_name = self._get_container_name(step_handler_context)
|
|
277
|
+
step_key = self._get_step_key(step_handler_context)
|
|
260
278
|
|
|
261
|
-
|
|
279
|
+
try:
|
|
280
|
+
container = client.containers.get(container_name)
|
|
281
|
+
except docker.errors.NotFound:
|
|
282
|
+
return CheckStepHealthResult.unhealthy(
|
|
283
|
+
reason=f"Docker container {container_name} for step {step_key} could not be found."
|
|
284
|
+
)
|
|
262
285
|
|
|
263
286
|
if container.status == "running":
|
|
264
287
|
return CheckStepHealthResult.healthy()
|
|
@@ -276,7 +299,7 @@ class DockerStepHandler(StepHandler):
|
|
|
276
299
|
return CheckStepHealthResult.healthy()
|
|
277
300
|
|
|
278
301
|
return CheckStepHealthResult.unhealthy(
|
|
279
|
-
reason=f"Container status is {container.status}. Return code is {
|
|
302
|
+
reason=f"Container status is {container.status}. Return code is {ret_code}."
|
|
280
303
|
)
|
|
281
304
|
|
|
282
305
|
def terminate_step(self, step_handler_context: StepHandlerContext) -> Iterator[DagsterEvent]:
|
|
@@ -285,12 +308,12 @@ class DockerStepHandler(StepHandler):
|
|
|
285
308
|
step_keys_to_execute = check.not_none(
|
|
286
309
|
step_handler_context.execute_step_args.step_keys_to_execute
|
|
287
310
|
)
|
|
288
|
-
assert (
|
|
289
|
-
|
|
290
|
-
)
|
|
311
|
+
assert len(step_keys_to_execute) == 1, (
|
|
312
|
+
"Terminating multiple steps is not currently supported"
|
|
313
|
+
)
|
|
291
314
|
step_key = step_keys_to_execute[0]
|
|
292
315
|
|
|
293
|
-
container_name = self._get_container_name(step_handler_context
|
|
316
|
+
container_name = self._get_container_name(step_handler_context)
|
|
294
317
|
|
|
295
318
|
yield DagsterEvent.engine_event(
|
|
296
319
|
step_handler_context.get_step_context(step_key),
|
|
@@ -302,4 +325,6 @@ class DockerStepHandler(StepHandler):
|
|
|
302
325
|
|
|
303
326
|
container = client.containers.get(container_name)
|
|
304
327
|
|
|
305
|
-
|
|
328
|
+
stop_timeout = container_context.container_kwargs.get("stop_timeout")
|
|
329
|
+
|
|
330
|
+
container.stop(timeout=stop_timeout)
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
1
5
|
import dagster._check as check
|
|
2
6
|
import docker
|
|
3
7
|
from dagster._core.launcher.base import (
|
|
@@ -7,16 +11,17 @@ from dagster._core.launcher.base import (
|
|
|
7
11
|
RunLauncher,
|
|
8
12
|
WorkerStatus,
|
|
9
13
|
)
|
|
10
|
-
from dagster._core.storage.
|
|
14
|
+
from dagster._core.storage.dagster_run import DagsterRun
|
|
11
15
|
from dagster._core.storage.tags import DOCKER_IMAGE_TAG
|
|
12
16
|
from dagster._core.utils import parse_env_var
|
|
13
17
|
from dagster._grpc.types import ExecuteRunArgs, ResumeRunArgs
|
|
14
18
|
from dagster._serdes import ConfigurableClass
|
|
19
|
+
from dagster._serdes.config_class import ConfigurableClassData
|
|
20
|
+
from typing_extensions import Self
|
|
15
21
|
|
|
22
|
+
from dagster_docker.container_context import DockerContainerContext
|
|
16
23
|
from dagster_docker.utils import DOCKER_CONFIG_SCHEMA, validate_docker_config, validate_docker_image
|
|
17
24
|
|
|
18
|
-
from .container_context import DockerContainerContext
|
|
19
|
-
|
|
20
25
|
DOCKER_CONTAINER_ID_TAG = "docker/container_id"
|
|
21
26
|
|
|
22
27
|
|
|
@@ -25,7 +30,7 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
25
30
|
|
|
26
31
|
def __init__(
|
|
27
32
|
self,
|
|
28
|
-
inst_data=None,
|
|
33
|
+
inst_data: Optional[ConfigurableClassData] = None,
|
|
29
34
|
image=None,
|
|
30
35
|
registry=None,
|
|
31
36
|
env_vars=None,
|
|
@@ -61,12 +66,14 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
61
66
|
def config_type(cls):
|
|
62
67
|
return DOCKER_CONFIG_SCHEMA
|
|
63
68
|
|
|
64
|
-
@
|
|
65
|
-
def from_config_value(
|
|
66
|
-
|
|
69
|
+
@classmethod
|
|
70
|
+
def from_config_value(
|
|
71
|
+
cls, inst_data: ConfigurableClassData, config_value: Mapping[str, Any]
|
|
72
|
+
) -> Self:
|
|
73
|
+
return cls(inst_data=inst_data, **config_value)
|
|
67
74
|
|
|
68
|
-
def get_container_context(self,
|
|
69
|
-
return DockerContainerContext.create_for_run(
|
|
75
|
+
def get_container_context(self, dagster_run: DagsterRun) -> DockerContainerContext:
|
|
76
|
+
return DockerContainerContext.create_for_run(dagster_run, self)
|
|
70
77
|
|
|
71
78
|
def _get_client(self, container_context: DockerContainerContext):
|
|
72
79
|
client = docker.client.from_env()
|
|
@@ -78,8 +85,8 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
78
85
|
)
|
|
79
86
|
return client
|
|
80
87
|
|
|
81
|
-
def _get_docker_image(self,
|
|
82
|
-
docker_image =
|
|
88
|
+
def _get_docker_image(self, job_code_origin):
|
|
89
|
+
docker_image = job_code_origin.repository_origin.container_image
|
|
83
90
|
|
|
84
91
|
if not docker_image:
|
|
85
92
|
docker_image = self.image
|
|
@@ -97,6 +104,17 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
97
104
|
|
|
98
105
|
client = self._get_client(container_context)
|
|
99
106
|
|
|
107
|
+
container_kwargs = {**container_context.container_kwargs}
|
|
108
|
+
labels = container_kwargs.pop("labels", {})
|
|
109
|
+
|
|
110
|
+
container_kwargs.pop("stop_timeout", None)
|
|
111
|
+
|
|
112
|
+
if isinstance(labels, list):
|
|
113
|
+
labels = {key: "" for key in labels}
|
|
114
|
+
|
|
115
|
+
labels["dagster/run_id"] = run.run_id
|
|
116
|
+
labels["dagster/job_name"] = run.job_name
|
|
117
|
+
|
|
100
118
|
try:
|
|
101
119
|
container = client.containers.create(
|
|
102
120
|
image=docker_image,
|
|
@@ -104,10 +122,11 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
104
122
|
detach=True,
|
|
105
123
|
environment=docker_env,
|
|
106
124
|
network=container_context.networks[0] if len(container_context.networks) else None,
|
|
107
|
-
|
|
125
|
+
labels=labels,
|
|
126
|
+
**container_kwargs,
|
|
108
127
|
)
|
|
109
128
|
|
|
110
|
-
except docker.errors.ImageNotFound:
|
|
129
|
+
except docker.errors.ImageNotFound: # pyright: ignore[reportAttributeAccessIssue]
|
|
111
130
|
client.images.pull(docker_image)
|
|
112
131
|
container = client.containers.create(
|
|
113
132
|
image=docker_image,
|
|
@@ -115,7 +134,8 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
115
134
|
detach=True,
|
|
116
135
|
environment=docker_env,
|
|
117
136
|
network=container_context.networks[0] if len(container_context.networks) else None,
|
|
118
|
-
|
|
137
|
+
labels=labels,
|
|
138
|
+
**container_kwargs,
|
|
119
139
|
)
|
|
120
140
|
|
|
121
141
|
if len(container_context.networks) > 1:
|
|
@@ -124,31 +144,26 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
124
144
|
network.connect(container)
|
|
125
145
|
|
|
126
146
|
self._instance.report_engine_event(
|
|
127
|
-
message=
|
|
128
|
-
|
|
129
|
-
container_id=container.id,
|
|
130
|
-
docker_image=docker_image,
|
|
131
|
-
)
|
|
132
|
-
),
|
|
133
|
-
pipeline_run=run,
|
|
147
|
+
message=f"Launching run in a new container {container.id} with image {docker_image}",
|
|
148
|
+
dagster_run=run,
|
|
134
149
|
cls=self.__class__,
|
|
135
150
|
)
|
|
136
151
|
|
|
137
152
|
self._instance.add_run_tags(
|
|
138
153
|
run.run_id,
|
|
139
|
-
{DOCKER_CONTAINER_ID_TAG: container.id, DOCKER_IMAGE_TAG: docker_image},
|
|
154
|
+
{DOCKER_CONTAINER_ID_TAG: container.id, DOCKER_IMAGE_TAG: docker_image}, # pyright: ignore[reportArgumentType]
|
|
140
155
|
)
|
|
141
156
|
|
|
142
157
|
container.start()
|
|
143
158
|
|
|
144
159
|
def launch_run(self, context: LaunchRunContext) -> None:
|
|
145
|
-
run = context.
|
|
146
|
-
|
|
147
|
-
docker_image = self._get_docker_image(
|
|
160
|
+
run = context.dagster_run
|
|
161
|
+
job_code_origin = check.not_none(context.job_code_origin)
|
|
162
|
+
docker_image = self._get_docker_image(job_code_origin)
|
|
148
163
|
|
|
149
164
|
command = ExecuteRunArgs(
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
job_origin=job_code_origin,
|
|
166
|
+
run_id=run.run_id,
|
|
152
167
|
instance_ref=self._instance.get_ref(),
|
|
153
168
|
).get_command_args()
|
|
154
169
|
|
|
@@ -159,20 +174,20 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
159
174
|
return True
|
|
160
175
|
|
|
161
176
|
def resume_run(self, context: ResumeRunContext) -> None:
|
|
162
|
-
run = context.
|
|
163
|
-
|
|
164
|
-
docker_image = self._get_docker_image(
|
|
177
|
+
run = context.dagster_run
|
|
178
|
+
job_code_origin = check.not_none(context.job_code_origin)
|
|
179
|
+
docker_image = self._get_docker_image(job_code_origin)
|
|
165
180
|
|
|
166
181
|
command = ResumeRunArgs(
|
|
167
|
-
|
|
168
|
-
|
|
182
|
+
job_origin=job_code_origin,
|
|
183
|
+
run_id=run.run_id,
|
|
169
184
|
instance_ref=self._instance.get_ref(),
|
|
170
185
|
).get_command_args()
|
|
171
186
|
|
|
172
187
|
self._launch_container_with_command(run, docker_image, command)
|
|
173
188
|
|
|
174
189
|
def _get_container(self, run):
|
|
175
|
-
if not run
|
|
190
|
+
if not run:
|
|
176
191
|
return None
|
|
177
192
|
|
|
178
193
|
container_id = run.tags.get(DOCKER_CONTAINER_ID_TAG)
|
|
@@ -184,24 +199,31 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
184
199
|
|
|
185
200
|
try:
|
|
186
201
|
return self._get_client(container_context).containers.get(container_id)
|
|
187
|
-
except
|
|
202
|
+
except docker.errors.NotFound: # pyright: ignore[reportAttributeAccessIssue]
|
|
188
203
|
return None
|
|
189
204
|
|
|
190
205
|
def terminate(self, run_id):
|
|
191
206
|
run = self._instance.get_run_by_id(run_id)
|
|
207
|
+
|
|
208
|
+
if not run or run.is_finished:
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
self._instance.report_run_canceling(run)
|
|
212
|
+
|
|
192
213
|
container = self._get_container(run)
|
|
193
214
|
|
|
215
|
+
container_context = self.get_container_context(run)
|
|
216
|
+
stop_timeout = container_context.container_kwargs.get("stop_timeout")
|
|
217
|
+
|
|
194
218
|
if not container:
|
|
195
219
|
self._instance.report_engine_event(
|
|
196
220
|
message="Unable to get docker container to send termination request to.",
|
|
197
|
-
|
|
221
|
+
dagster_run=run,
|
|
198
222
|
cls=self.__class__,
|
|
199
223
|
)
|
|
200
224
|
return False
|
|
201
225
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
container.stop()
|
|
226
|
+
container.stop(timeout=stop_timeout)
|
|
205
227
|
|
|
206
228
|
return True
|
|
207
229
|
|
|
@@ -210,11 +232,22 @@ class DockerRunLauncher(RunLauncher, ConfigurableClass):
|
|
|
210
232
|
return True
|
|
211
233
|
|
|
212
234
|
def check_run_worker_health(self, run: DagsterRun):
|
|
235
|
+
container_id = run.tags.get(DOCKER_CONTAINER_ID_TAG)
|
|
236
|
+
|
|
237
|
+
if not container_id:
|
|
238
|
+
return CheckRunHealthResult(WorkerStatus.NOT_FOUND, msg="No container ID tag for run.")
|
|
239
|
+
|
|
213
240
|
container = self._get_container(run)
|
|
214
241
|
if container is None:
|
|
215
|
-
return CheckRunHealthResult(
|
|
242
|
+
return CheckRunHealthResult(
|
|
243
|
+
WorkerStatus.NOT_FOUND, msg=f"Could not find container with ID {container_id}."
|
|
244
|
+
)
|
|
216
245
|
if container.status == "running":
|
|
217
246
|
return CheckRunHealthResult(WorkerStatus.RUNNING)
|
|
218
|
-
|
|
219
|
-
|
|
247
|
+
|
|
248
|
+
container_state = container.attrs.get("State")
|
|
249
|
+
failure_string = f"Container status is {container.status}." + (
|
|
250
|
+
f" Container state: {json.dumps(container_state)}" if container_state else ""
|
|
220
251
|
)
|
|
252
|
+
|
|
253
|
+
return CheckRunHealthResult(WorkerStatus.FAILED, msg=failure_string)
|
dagster_docker/ops/__init__.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
from typing import Any, Optional
|
|
2
3
|
|
|
3
4
|
import docker
|
|
5
|
+
import docker.errors
|
|
4
6
|
from dagster import Field, In, Nothing, OpExecutionContext, StringSource, op
|
|
5
|
-
from dagster._annotations import
|
|
7
|
+
from dagster._annotations import beta
|
|
6
8
|
from dagster._core.utils import parse_env_var
|
|
7
|
-
from
|
|
9
|
+
from dagster_shared.serdes.utils import hash_str
|
|
8
10
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
11
|
+
from dagster_docker.container_context import DockerContainerContext
|
|
12
|
+
from dagster_docker.docker_run_launcher import DockerRunLauncher
|
|
13
|
+
from dagster_docker.utils import DOCKER_CONFIG_SCHEMA, validate_docker_image
|
|
12
14
|
|
|
13
15
|
DOCKER_CONTAINER_OP_CONFIG = {
|
|
14
16
|
**DOCKER_CONFIG_SCHEMA,
|
|
@@ -44,7 +46,6 @@ def _get_client(docker_container_context: DockerContainerContext):
|
|
|
44
46
|
def _get_container_name(run_id, op_name, retry_number):
|
|
45
47
|
container_name = hash_str(run_id + op_name)
|
|
46
48
|
|
|
47
|
-
retry_number = retry_number
|
|
48
49
|
if retry_number > 0:
|
|
49
50
|
container_name = f"{container_name}-{retry_number}"
|
|
50
51
|
|
|
@@ -72,7 +73,7 @@ def _create_container(
|
|
|
72
73
|
)
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
@
|
|
76
|
+
@beta
|
|
76
77
|
def execute_docker_container(
|
|
77
78
|
context: OpExecutionContext,
|
|
78
79
|
image: str,
|
|
@@ -83,8 +84,7 @@ def execute_docker_container(
|
|
|
83
84
|
env_vars: Optional[Sequence[str]] = None,
|
|
84
85
|
container_kwargs: Optional[Mapping[str, Any]] = None,
|
|
85
86
|
):
|
|
86
|
-
"""
|
|
87
|
-
This function is a utility for executing a Docker container from within a Dagster op.
|
|
87
|
+
"""This function is a utility for executing a Docker container from within a Dagster op.
|
|
88
88
|
|
|
89
89
|
Args:
|
|
90
90
|
image (str): The image to use for the launched Docker container.
|
|
@@ -105,10 +105,12 @@ def execute_docker_container(
|
|
|
105
105
|
of available options.
|
|
106
106
|
"""
|
|
107
107
|
run_container_context = DockerContainerContext.create_for_run(
|
|
108
|
-
context.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
context.dagster_run,
|
|
109
|
+
(
|
|
110
|
+
context.instance.run_launcher
|
|
111
|
+
if isinstance(context.instance.run_launcher, DockerRunLauncher)
|
|
112
|
+
else None
|
|
113
|
+
),
|
|
112
114
|
)
|
|
113
115
|
|
|
114
116
|
validate_docker_image(image)
|
|
@@ -139,7 +141,7 @@ def execute_docker_container(
|
|
|
139
141
|
container.start()
|
|
140
142
|
|
|
141
143
|
for line in container.logs(stdout=True, stderr=True, stream=True, follow=True):
|
|
142
|
-
print(line)
|
|
144
|
+
print(line) # noqa: T201
|
|
143
145
|
|
|
144
146
|
exit_status = container.wait()["StatusCode"]
|
|
145
147
|
|
|
@@ -148,10 +150,9 @@ def execute_docker_container(
|
|
|
148
150
|
|
|
149
151
|
|
|
150
152
|
@op(ins={"start_after": In(Nothing)}, config_schema=DOCKER_CONTAINER_OP_CONFIG)
|
|
151
|
-
@
|
|
153
|
+
@beta
|
|
152
154
|
def docker_container_op(context):
|
|
153
|
-
"""
|
|
154
|
-
An op that runs a Docker container using the docker Python API.
|
|
155
|
+
"""An op that runs a Docker container using the docker Python API.
|
|
155
156
|
|
|
156
157
|
Contrast with the `docker_executor`, which runs each Dagster op in a Dagster job in its
|
|
157
158
|
own Docker container.
|
dagster_docker/pipes.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
|
|
5
|
+
import docker
|
|
6
|
+
import docker.errors
|
|
7
|
+
from dagster import (
|
|
8
|
+
OpExecutionContext,
|
|
9
|
+
_check as check,
|
|
10
|
+
)
|
|
11
|
+
from dagster._core.definitions.resource_annotation import TreatAsResourceParam
|
|
12
|
+
from dagster._core.execution.context.asset_execution_context import AssetExecutionContext
|
|
13
|
+
from dagster._core.pipes.client import (
|
|
14
|
+
PipesClient,
|
|
15
|
+
PipesClientCompletedInvocation,
|
|
16
|
+
PipesContextInjector,
|
|
17
|
+
PipesMessageReader,
|
|
18
|
+
)
|
|
19
|
+
from dagster._core.pipes.context import PipesMessageHandler
|
|
20
|
+
from dagster._core.pipes.utils import (
|
|
21
|
+
PipesEnvContextInjector,
|
|
22
|
+
extract_message_or_forward_to_stdout,
|
|
23
|
+
open_pipes_session,
|
|
24
|
+
)
|
|
25
|
+
from dagster_pipes import DagsterPipesError, PipesDefaultMessageWriter, PipesExtras, PipesParams
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PipesDockerLogsMessageReader(PipesMessageReader):
|
|
29
|
+
@contextmanager
|
|
30
|
+
def read_messages(
|
|
31
|
+
self,
|
|
32
|
+
handler: PipesMessageHandler,
|
|
33
|
+
) -> Iterator[PipesParams]:
|
|
34
|
+
self._handler = handler
|
|
35
|
+
try:
|
|
36
|
+
yield {PipesDefaultMessageWriter.STDIO_KEY: PipesDefaultMessageWriter.STDERR}
|
|
37
|
+
finally:
|
|
38
|
+
self._handler = None
|
|
39
|
+
|
|
40
|
+
def consume_docker_logs(self, container) -> None:
|
|
41
|
+
handler = check.not_none(
|
|
42
|
+
self._handler, "Can only consume logs within context manager scope."
|
|
43
|
+
)
|
|
44
|
+
for log_line in container.logs(stdout=True, stderr=True, stream=True, follow=True):
|
|
45
|
+
if isinstance(log_line, bytes):
|
|
46
|
+
log_entry = log_line.decode("utf-8")
|
|
47
|
+
elif isinstance(log_line, str):
|
|
48
|
+
log_entry = log_line
|
|
49
|
+
else:
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
extract_message_or_forward_to_stdout(handler, log_entry)
|
|
53
|
+
|
|
54
|
+
def no_messages_debug_text(self) -> str:
|
|
55
|
+
return "Attempted to read messages by extracting them from docker logs directly."
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class PipesDockerClient(PipesClient, TreatAsResourceParam):
|
|
59
|
+
"""A pipes client that runs external processes in docker containers.
|
|
60
|
+
|
|
61
|
+
By default context is injected via environment variables and messages are parsed out of the
|
|
62
|
+
log stream, with other logs forwarded to stdout of the orchestration process.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
env (Optional[Mapping[str, str]]): An optional dict of environment variables to pass to the
|
|
66
|
+
container.
|
|
67
|
+
register (Optional[Mapping[str, str]]): An optional dict of registry credentials to login to
|
|
68
|
+
the docker client.
|
|
69
|
+
context_injector (Optional[PipesContextInjector]): A context injector to use to inject
|
|
70
|
+
context into the docker container process. Defaults to :py:class:`PipesEnvContextInjector`.
|
|
71
|
+
message_reader (Optional[PipesMessageReader]): A message reader to use to read messages
|
|
72
|
+
from the docker container process. Defaults to :py:class:`DockerLogsMessageReader`.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
env: Optional[Mapping[str, str]] = None,
|
|
78
|
+
registry: Optional[Mapping[str, str]] = None,
|
|
79
|
+
context_injector: Optional[PipesContextInjector] = None,
|
|
80
|
+
message_reader: Optional[PipesMessageReader] = None,
|
|
81
|
+
):
|
|
82
|
+
self.env = check.opt_mapping_param(env, "env", key_type=str, value_type=str)
|
|
83
|
+
self.registry = check.opt_mapping_param(registry, "registry", key_type=str, value_type=str)
|
|
84
|
+
self.context_injector = (
|
|
85
|
+
check.opt_inst_param(
|
|
86
|
+
context_injector,
|
|
87
|
+
"context_injector",
|
|
88
|
+
PipesContextInjector,
|
|
89
|
+
)
|
|
90
|
+
or PipesEnvContextInjector()
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
self.message_reader = (
|
|
94
|
+
check.opt_inst_param(message_reader, "message_reader", PipesMessageReader)
|
|
95
|
+
or PipesDockerLogsMessageReader()
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def _is_dagster_maintained(cls) -> bool:
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
def run( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
103
|
+
self,
|
|
104
|
+
*,
|
|
105
|
+
context: Union[OpExecutionContext, AssetExecutionContext],
|
|
106
|
+
image: str,
|
|
107
|
+
extras: Optional[PipesExtras] = None,
|
|
108
|
+
command: Optional[Union[str, Sequence[str]]] = None,
|
|
109
|
+
env: Optional[Mapping[str, str]] = None,
|
|
110
|
+
registry: Optional[Mapping[str, str]] = None,
|
|
111
|
+
container_kwargs: Optional[Mapping[str, Any]] = None,
|
|
112
|
+
) -> PipesClientCompletedInvocation:
|
|
113
|
+
"""Create a docker container and run it to completion, enriched with the pipes protocol.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
image (str):
|
|
117
|
+
The image for the container to use.
|
|
118
|
+
command (Optional[Union[str, Sequence[str]]]):
|
|
119
|
+
The command for the container use.
|
|
120
|
+
env (Optional[Mapping[str,str]]):
|
|
121
|
+
A mapping of environment variable names to values to set on the first
|
|
122
|
+
container in the pod spec, on top of those configured on resource.
|
|
123
|
+
registry (Optional[Mapping[str, str]]:
|
|
124
|
+
A mapping containing url, username, and password to be used
|
|
125
|
+
with docker client login.
|
|
126
|
+
container_kwargs (Optional[Mapping[str, Any]]:
|
|
127
|
+
Arguments to be forwarded to docker client containers.create.
|
|
128
|
+
extras (Optional[PipesExtras]):
|
|
129
|
+
Extra values to pass along as part of the ext protocol.
|
|
130
|
+
context_injector (Optional[PipesContextInjector]):
|
|
131
|
+
Override the default ext protocol context injection.
|
|
132
|
+
message_reader (Optional[PipesMessageReader]):
|
|
133
|
+
Override the default ext protocol message reader.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
PipesClientCompletedInvocation: Wrapper containing results reported by the external
|
|
137
|
+
process.
|
|
138
|
+
"""
|
|
139
|
+
with open_pipes_session(
|
|
140
|
+
context=context,
|
|
141
|
+
context_injector=self.context_injector,
|
|
142
|
+
message_reader=self.message_reader,
|
|
143
|
+
extras=extras,
|
|
144
|
+
) as pipes_session:
|
|
145
|
+
client = docker.client.from_env()
|
|
146
|
+
registry = registry or self.registry
|
|
147
|
+
if registry:
|
|
148
|
+
client.login(
|
|
149
|
+
registry=registry["url"],
|
|
150
|
+
username=registry["username"],
|
|
151
|
+
password=registry["password"],
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
container = self._create_container(
|
|
156
|
+
client=client,
|
|
157
|
+
image=image,
|
|
158
|
+
command=command,
|
|
159
|
+
env=env,
|
|
160
|
+
open_pipes_session_env=pipes_session.get_bootstrap_env_vars(),
|
|
161
|
+
container_kwargs=container_kwargs,
|
|
162
|
+
)
|
|
163
|
+
except docker.errors.ImageNotFound:
|
|
164
|
+
client.images.pull(image)
|
|
165
|
+
container = self._create_container(
|
|
166
|
+
client=client,
|
|
167
|
+
image=image,
|
|
168
|
+
command=command,
|
|
169
|
+
env=env,
|
|
170
|
+
open_pipes_session_env=pipes_session.get_bootstrap_env_vars(),
|
|
171
|
+
container_kwargs=container_kwargs,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
result = container.start()
|
|
175
|
+
try:
|
|
176
|
+
if isinstance(self.message_reader, PipesDockerLogsMessageReader):
|
|
177
|
+
self.message_reader.consume_docker_logs(container)
|
|
178
|
+
|
|
179
|
+
result = container.wait()
|
|
180
|
+
if result["StatusCode"] != 0:
|
|
181
|
+
raise DagsterPipesError(f"Container exited with non-zero status code: {result}")
|
|
182
|
+
finally:
|
|
183
|
+
container.stop()
|
|
184
|
+
return PipesClientCompletedInvocation(pipes_session)
|
|
185
|
+
|
|
186
|
+
def _create_container(
|
|
187
|
+
self,
|
|
188
|
+
client,
|
|
189
|
+
image: str,
|
|
190
|
+
command: Optional[Union[str, Sequence[str]]],
|
|
191
|
+
env: Optional[Mapping[str, str]],
|
|
192
|
+
container_kwargs: Optional[Mapping[str, Any]],
|
|
193
|
+
open_pipes_session_env: Mapping[str, str],
|
|
194
|
+
):
|
|
195
|
+
kwargs = dict(container_kwargs or {})
|
|
196
|
+
kwargs_env = kwargs.pop("environment", {})
|
|
197
|
+
return client.containers.create(
|
|
198
|
+
image=image,
|
|
199
|
+
command=command,
|
|
200
|
+
detach=True,
|
|
201
|
+
environment={
|
|
202
|
+
**open_pipes_session_env,
|
|
203
|
+
**(self.env or {}),
|
|
204
|
+
**(env or {}),
|
|
205
|
+
**kwargs_env,
|
|
206
|
+
},
|
|
207
|
+
**kwargs,
|
|
208
|
+
)
|
dagster_docker/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
partial
|
dagster_docker/utils.py
CHANGED
|
@@ -6,7 +6,7 @@ from dagster import (
|
|
|
6
6
|
from dagster._utils.merger import merge_dicts
|
|
7
7
|
from docker_image import reference
|
|
8
8
|
|
|
9
|
-
from .container_context import DOCKER_CONTAINER_CONTEXT_SCHEMA
|
|
9
|
+
from dagster_docker.container_context import DOCKER_CONTAINER_CONTEXT_SCHEMA
|
|
10
10
|
|
|
11
11
|
DOCKER_CONFIG_SCHEMA = merge_dicts(
|
|
12
12
|
{
|
|
@@ -55,8 +55,4 @@ def validate_docker_image(docker_image):
|
|
|
55
55
|
# validate that the docker image name is valid
|
|
56
56
|
reference.Reference.parse(docker_image)
|
|
57
57
|
except Exception as e:
|
|
58
|
-
raise Exception(
|
|
59
|
-
"Docker image name {docker_image} is not correctly formatted".format(
|
|
60
|
-
docker_image=docker_image
|
|
61
|
-
)
|
|
62
|
-
) from e
|
|
58
|
+
raise Exception(f"Docker image name {docker_image} is not correctly formatted") from e
|
dagster_docker/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.28.5"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dagster-docker
|
|
3
|
+
Version: 0.28.5
|
|
4
|
+
Summary: A Dagster integration for docker
|
|
5
|
+
Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-docker
|
|
6
|
+
Author: Dagster Labs
|
|
7
|
+
Author-email: hello@dagsterlabs.com
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.10,<3.14
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: dagster==1.12.5
|
|
18
|
+
Requires-Dist: docker
|
|
19
|
+
Requires-Dist: docker-image-py
|
|
20
|
+
Provides-Extra: test
|
|
21
|
+
Requires-Dist: flaky; extra == "test"
|
|
22
|
+
Requires-Dist: botocore>=1.21.49; extra == "test"
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
Dynamic: provides-extra
|
|
30
|
+
Dynamic: requires-dist
|
|
31
|
+
Dynamic: requires-python
|
|
32
|
+
Dynamic: summary
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
dagster_docker/__init__.py,sha256=rnJ8jarKzaUQl3kIXVFR9h2kNinX0Enjx_mIROBgHdY,624
|
|
2
|
+
dagster_docker/container_context.py,sha256=pbM6btkjXppI5mXaU-2YhRSUTkCQfhzvPHz72WzM3Ec,6485
|
|
3
|
+
dagster_docker/docker_executor.py,sha256=i9wszCPICeduvseI4OPfYOob8NShlBnG0ZLlD0p8EKY,12826
|
|
4
|
+
dagster_docker/docker_run_launcher.py,sha256=3rGXZ1wp9gbPyn1iP5XLiTxF-JzazCVpt50qvvfTSm8,8458
|
|
5
|
+
dagster_docker/pipes.py,sha256=Ibp5ZqYmISxHZBaOwVwed2phCZaRfimmDSG87lVrRj0,8242
|
|
6
|
+
dagster_docker/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
7
|
+
dagster_docker/utils.py,sha256=oo3JziSJL0CrHMtvQL9KA3OUEWTBEHA1PdY4sqotZy0,1821
|
|
8
|
+
dagster_docker/version.py,sha256=SdGElACDuKJX1c_iqPcfu_quaKOShee-DePM8qfbbJU,23
|
|
9
|
+
dagster_docker/ops/__init__.py,sha256=-86lzmRMHY6zwn33K7hP1_CI0Z14c4-8urBKl7tlIA4,161
|
|
10
|
+
dagster_docker/ops/docker_container_op.py,sha256=zzhdaKlAhBWn1bAHWpy-9ym6evxK1eZniSfACuR7E_E,6434
|
|
11
|
+
dagster_docker-0.28.5.dist-info/licenses/LICENSE,sha256=4lsMW-RCvfVD4_F57wrmpe3vX1xwUk_OAKKmV_XT7Z0,11348
|
|
12
|
+
dagster_docker-0.28.5.dist-info/METADATA,sha256=ZhrMexywE8Dz17ILRMTY-fzxDhrlr1MRYm-5yfqBA8c,1043
|
|
13
|
+
dagster_docker-0.28.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
dagster_docker-0.28.5.dist-info/top_level.txt,sha256=FvMtaf9uYIb-jIVJgoiNi1tJ31JZmvh55GxzszLahKs,15
|
|
15
|
+
dagster_docker-0.28.5.dist-info/RECORD,,
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2025 Dagster Labs, Inc.
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: dagster-docker
|
|
3
|
-
Version: 0.17.19
|
|
4
|
-
Summary: A Dagster integration for docker
|
|
5
|
-
Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-docker
|
|
6
|
-
Author: Elementl
|
|
7
|
-
Author-email: hello@elementl.com
|
|
8
|
-
License: Apache-2.0
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
-
Classifier: Operating System :: OS Independent
|
|
16
|
-
License-File: LICENSE
|
|
17
|
-
Requires-Dist: dagster (==1.1.19)
|
|
18
|
-
Requires-Dist: docker
|
|
19
|
-
Requires-Dist: docker-image-py
|
|
20
|
-
|
|
21
|
-
UNKNOWN
|
|
22
|
-
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
dagster_docker/__init__.py,sha256=FVXqcMHKY8GXFaqIl5aTpX5fHHTsRu4dGJ6EYkusfdM,421
|
|
2
|
-
dagster_docker/container_context.py,sha256=ytE70Wj2emHOopviwZ6aE7bkc3sywCvJTo4T9WVJVmo,6484
|
|
3
|
-
dagster_docker/docker_executor.py,sha256=gMbZLVOGJvMuWIED7Lmhf4iT5JivW2fxtRX79B24Eqw,11815
|
|
4
|
-
dagster_docker/docker_run_launcher.py,sha256=S5_EFHOOo4q82BipDN1mRHX0GJa1H3wucLz44wqnrdo,7220
|
|
5
|
-
dagster_docker/utils.py,sha256=pviR2koL8w_UQq14UY2wquT1zcy5VDEg9sOteybURsY,1892
|
|
6
|
-
dagster_docker/version.py,sha256=LKtJnPhS5GRuhOh9obHGnDlncpF9oAEamdbIKdiL3Tw,24
|
|
7
|
-
dagster_docker/ops/__init__.py,sha256=ZGKgOE1FC-vGfR4bk2kg4FXSg4LVsV2PzIK_XyAUqOk,143
|
|
8
|
-
dagster_docker/ops/docker_container_op.py,sha256=jJFwgFnbNlCyq2g6AiUI41Rd_0WSMhhtyBQeez9Ay1k,6362
|
|
9
|
-
dagster_docker-0.17.19.dist-info/LICENSE,sha256=-gtoVIAZYUHYmNHISZg982FI4Oh19mV1nxgTVW8eCB8,11344
|
|
10
|
-
dagster_docker-0.17.19.dist-info/METADATA,sha256=FHEWIB02zH3sDvYolvv0XWAM3YdHwhuWjY6f-ipc7fk,721
|
|
11
|
-
dagster_docker-0.17.19.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92
|
|
12
|
-
dagster_docker-0.17.19.dist-info/top_level.txt,sha256=FvMtaf9uYIb-jIVJgoiNi1tJ31JZmvh55GxzszLahKs,15
|
|
13
|
-
dagster_docker-0.17.19.dist-info/RECORD,,
|
|
File without changes
|