dagster-cloud 1.8.2__py3-none-any.whl → 1.12.6__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_cloud/__init__.py +3 -3
- dagster_cloud/agent/__init__.py +4 -4
- dagster_cloud/agent/cli/__init__.py +56 -17
- dagster_cloud/agent/dagster_cloud_agent.py +360 -172
- dagster_cloud/agent/instrumentation/__init__.py +0 -0
- dagster_cloud/agent/instrumentation/constants.py +2 -0
- dagster_cloud/agent/instrumentation/run_launch.py +23 -0
- dagster_cloud/agent/instrumentation/schedule.py +34 -0
- dagster_cloud/agent/instrumentation/sensor.py +34 -0
- dagster_cloud/anomaly_detection/__init__.py +2 -2
- dagster_cloud/anomaly_detection/defs.py +17 -12
- dagster_cloud/anomaly_detection/types.py +3 -3
- dagster_cloud/api/dagster_cloud_api.py +209 -293
- dagster_cloud/auth/constants.py +21 -5
- dagster_cloud/batching/__init__.py +1 -0
- dagster_cloud/batching/batcher.py +210 -0
- dagster_cloud/dagster_insights/__init__.py +12 -6
- dagster_cloud/dagster_insights/bigquery/bigquery_utils.py +3 -2
- dagster_cloud/dagster_insights/bigquery/dbt_wrapper.py +39 -12
- dagster_cloud/dagster_insights/bigquery/insights_bigquery_resource.py +8 -6
- dagster_cloud/dagster_insights/insights_utils.py +18 -8
- dagster_cloud/dagster_insights/metrics_utils.py +12 -12
- dagster_cloud/dagster_insights/snowflake/dagster_snowflake_insights.py +5 -12
- dagster_cloud/dagster_insights/snowflake/dbt_wrapper.py +34 -8
- dagster_cloud/dagster_insights/snowflake/definitions.py +38 -12
- dagster_cloud/dagster_insights/snowflake/insights_snowflake_resource.py +11 -23
- dagster_cloud/definitions/__init__.py +0 -0
- dagster_cloud/definitions/job_selection.py +36 -0
- dagster_cloud/execution/cloud_run_launcher/k8s.py +1 -1
- dagster_cloud/execution/cloud_run_launcher/process.py +3 -3
- dagster_cloud/execution/monitoring/__init__.py +27 -33
- dagster_cloud/execution/utils/process.py +3 -3
- dagster_cloud/instance/__init__.py +125 -38
- dagster_cloud/instrumentation/__init__.py +32 -0
- dagster_cloud/metadata/source_code.py +13 -8
- dagster_cloud/metrics/__init__.py +0 -0
- dagster_cloud/metrics/tracer.py +59 -0
- dagster_cloud/opentelemetry/__init__.py +0 -0
- dagster_cloud/opentelemetry/config/__init__.py +73 -0
- dagster_cloud/opentelemetry/config/exporter.py +81 -0
- dagster_cloud/opentelemetry/config/log_record_processor.py +40 -0
- dagster_cloud/opentelemetry/config/logging_handler.py +14 -0
- dagster_cloud/opentelemetry/config/meter_provider.py +9 -0
- dagster_cloud/opentelemetry/config/metric_reader.py +39 -0
- dagster_cloud/opentelemetry/controller.py +319 -0
- dagster_cloud/opentelemetry/enum.py +58 -0
- dagster_cloud/opentelemetry/factories/__init__.py +1 -0
- dagster_cloud/opentelemetry/factories/logs.py +113 -0
- dagster_cloud/opentelemetry/factories/metrics.py +121 -0
- dagster_cloud/opentelemetry/metrics/__init__.py +0 -0
- dagster_cloud/opentelemetry/metrics/meter.py +140 -0
- dagster_cloud/opentelemetry/observers/__init__.py +0 -0
- dagster_cloud/opentelemetry/observers/dagster_exception_handler.py +40 -0
- dagster_cloud/opentelemetry/observers/execution_observer.py +178 -0
- dagster_cloud/pex/grpc/__generated__/multi_pex_api_pb2.pyi +175 -0
- dagster_cloud/pex/grpc/__init__.py +2 -2
- dagster_cloud/pex/grpc/client.py +4 -4
- dagster_cloud/pex/grpc/compile.py +2 -2
- dagster_cloud/pex/grpc/server/__init__.py +2 -2
- dagster_cloud/pex/grpc/server/cli/__init__.py +31 -19
- dagster_cloud/pex/grpc/server/manager.py +60 -42
- dagster_cloud/pex/grpc/server/registry.py +28 -21
- dagster_cloud/pex/grpc/server/server.py +23 -14
- dagster_cloud/pex/grpc/types.py +5 -5
- dagster_cloud/py.typed +0 -0
- dagster_cloud/secrets/__init__.py +1 -1
- dagster_cloud/secrets/loader.py +3 -3
- dagster_cloud/serverless/__init__.py +1 -1
- dagster_cloud/serverless/io_manager.py +36 -53
- dagster_cloud/storage/client.py +54 -17
- dagster_cloud/storage/compute_logs/__init__.py +3 -1
- dagster_cloud/storage/compute_logs/compute_log_manager.py +22 -17
- dagster_cloud/storage/defs_state/__init__.py +3 -0
- dagster_cloud/storage/defs_state/queries.py +15 -0
- dagster_cloud/storage/defs_state/storage.py +113 -0
- dagster_cloud/storage/event_logs/__init__.py +3 -1
- dagster_cloud/storage/event_logs/queries.py +102 -4
- dagster_cloud/storage/event_logs/storage.py +266 -73
- dagster_cloud/storage/event_logs/utils.py +88 -7
- dagster_cloud/storage/runs/__init__.py +1 -1
- dagster_cloud/storage/runs/queries.py +17 -2
- dagster_cloud/storage/runs/storage.py +88 -42
- dagster_cloud/storage/schedules/__init__.py +1 -1
- dagster_cloud/storage/schedules/storage.py +6 -8
- dagster_cloud/storage/tags.py +66 -1
- dagster_cloud/util/__init__.py +10 -12
- dagster_cloud/util/errors.py +49 -64
- dagster_cloud/version.py +1 -1
- dagster_cloud/workspace/config_schema/__init__.py +55 -13
- dagster_cloud/workspace/docker/__init__.py +76 -25
- dagster_cloud/workspace/docker/utils.py +1 -1
- dagster_cloud/workspace/ecs/__init__.py +1 -1
- dagster_cloud/workspace/ecs/client.py +51 -33
- dagster_cloud/workspace/ecs/launcher.py +76 -22
- dagster_cloud/workspace/ecs/run_launcher.py +3 -3
- dagster_cloud/workspace/ecs/utils.py +14 -5
- dagster_cloud/workspace/kubernetes/__init__.py +1 -1
- dagster_cloud/workspace/kubernetes/launcher.py +61 -29
- dagster_cloud/workspace/kubernetes/utils.py +34 -22
- dagster_cloud/workspace/user_code_launcher/__init__.py +5 -3
- dagster_cloud/workspace/user_code_launcher/process.py +16 -14
- dagster_cloud/workspace/user_code_launcher/user_code_launcher.py +552 -172
- dagster_cloud/workspace/user_code_launcher/utils.py +105 -1
- {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/METADATA +48 -42
- dagster_cloud-1.12.6.dist-info/RECORD +134 -0
- {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/WHEEL +1 -1
- dagster_cloud-1.8.2.dist-info/RECORD +0 -100
- {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/top_level.txt +0 -0
dagster_cloud/util/errors.py
CHANGED
|
@@ -1,66 +1,51 @@
|
|
|
1
|
-
from
|
|
2
|
-
from dagster._utils.error import SerializableErrorInfo
|
|
3
|
-
|
|
4
|
-
ERROR_CLASS_NAME_SIZE_LIMIT = 1000
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def truncate_serialized_error(
|
|
8
|
-
error_info: SerializableErrorInfo, field_size_limit: int, max_depth: int
|
|
9
|
-
):
|
|
10
|
-
if error_info.cause:
|
|
11
|
-
if max_depth == 0:
|
|
12
|
-
new_cause = (
|
|
13
|
-
error_info.cause
|
|
14
|
-
if len(serialize_value(error_info.cause)) <= field_size_limit
|
|
15
|
-
else SerializableErrorInfo(
|
|
16
|
-
message="(Cause truncated due to size limitations)",
|
|
17
|
-
stack=[],
|
|
18
|
-
cls_name=None,
|
|
19
|
-
)
|
|
20
|
-
)
|
|
21
|
-
else:
|
|
22
|
-
new_cause = truncate_serialized_error(
|
|
23
|
-
error_info.cause, field_size_limit, max_depth=max_depth - 1
|
|
24
|
-
)
|
|
25
|
-
error_info = error_info._replace(cause=new_cause)
|
|
26
|
-
|
|
27
|
-
if error_info.context:
|
|
28
|
-
if max_depth == 0:
|
|
29
|
-
new_context = (
|
|
30
|
-
error_info.context
|
|
31
|
-
if len(serialize_value(error_info.context)) <= field_size_limit
|
|
32
|
-
else SerializableErrorInfo(
|
|
33
|
-
message="(Context truncated due to size limitations)",
|
|
34
|
-
stack=[],
|
|
35
|
-
cls_name=None,
|
|
36
|
-
)
|
|
37
|
-
)
|
|
38
|
-
else:
|
|
39
|
-
new_context = truncate_serialized_error(
|
|
40
|
-
error_info.context, field_size_limit, max_depth=max_depth - 1
|
|
41
|
-
)
|
|
42
|
-
error_info = error_info._replace(context=new_context)
|
|
1
|
+
from collections.abc import Sequence
|
|
43
2
|
|
|
44
|
-
|
|
45
|
-
truncated_stack = []
|
|
46
|
-
for stack_elem in error_info.stack:
|
|
47
|
-
stack_size_so_far += len(stack_elem)
|
|
48
|
-
if stack_size_so_far > field_size_limit:
|
|
49
|
-
truncated_stack.append("(TRUNCATED)")
|
|
50
|
-
break
|
|
51
|
-
|
|
52
|
-
truncated_stack.append(stack_elem)
|
|
53
|
-
|
|
54
|
-
error_info = error_info._replace(stack=truncated_stack)
|
|
55
|
-
|
|
56
|
-
if len(error_info.message) > field_size_limit:
|
|
57
|
-
error_info = error_info._replace(
|
|
58
|
-
message=error_info.message[:field_size_limit] + " (TRUNCATED)"
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
if error_info.cls_name and len(error_info.cls_name) > ERROR_CLASS_NAME_SIZE_LIMIT:
|
|
62
|
-
error_info = error_info._replace(
|
|
63
|
-
cls_name=error_info.cls_name[:ERROR_CLASS_NAME_SIZE_LIMIT] + " (TRUNCATED)"
|
|
64
|
-
)
|
|
3
|
+
from dagster._utils.error import SerializableErrorInfo
|
|
65
4
|
|
|
66
|
-
|
|
5
|
+
DAGSTER_FRAMEWORK_SUBSTRINGS = [
|
|
6
|
+
"/site-packages/dagster/",
|
|
7
|
+
"/python_modules/dagster/dagster",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def remove_dagster_framework_lines_from_serializable_exc_info(error_info: SerializableErrorInfo):
|
|
12
|
+
return error_info._replace(
|
|
13
|
+
stack=remove_dagster_framework_lines_from_stack_trace(error_info.stack),
|
|
14
|
+
cause=(
|
|
15
|
+
remove_dagster_framework_lines_from_serializable_exc_info(error_info.cause)
|
|
16
|
+
if error_info.cause
|
|
17
|
+
else None
|
|
18
|
+
),
|
|
19
|
+
context=(
|
|
20
|
+
remove_dagster_framework_lines_from_serializable_exc_info(error_info.context)
|
|
21
|
+
if error_info.context
|
|
22
|
+
else None
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def remove_dagster_framework_lines_from_stack_trace(
|
|
28
|
+
stack: Sequence[str],
|
|
29
|
+
) -> Sequence[str]:
|
|
30
|
+
# Remove lines until you find the first non-dagster framework line
|
|
31
|
+
|
|
32
|
+
for i in range(len(stack)):
|
|
33
|
+
if not _line_contains_dagster_framework_file(stack[i]):
|
|
34
|
+
return stack[i:]
|
|
35
|
+
|
|
36
|
+
# Return the full stack trace if its all Dagster framework lines,
|
|
37
|
+
# to not be left with an empty stack trace
|
|
38
|
+
return stack
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _line_contains_dagster_framework_file(line: str):
|
|
42
|
+
# stack trace line starts with something like
|
|
43
|
+
# File "/usr/local/lib/python3.11/site-packages/dagster/_core/execution/plan/utils.py",
|
|
44
|
+
split_by_comma = line.split(",")
|
|
45
|
+
if not split_by_comma:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
file_portion = split_by_comma[0]
|
|
49
|
+
return any(
|
|
50
|
+
framework_substring in file_portion for framework_substring in DAGSTER_FRAMEWORK_SUBSTRINGS
|
|
51
|
+
)
|
dagster_cloud/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.12.6"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any,
|
|
1
|
+
from typing import Any, Optional, cast
|
|
2
2
|
|
|
3
3
|
from dagster import (
|
|
4
4
|
Enum as DagsterEnum,
|
|
@@ -13,15 +13,15 @@ from dagster import (
|
|
|
13
13
|
)
|
|
14
14
|
from dagster._config import EvaluationError, StringSource, validate_config
|
|
15
15
|
|
|
16
|
-
from .docker import SHARED_DOCKER_CONFIG
|
|
17
|
-
from .ecs import (
|
|
16
|
+
from dagster_cloud.workspace.config_schema.docker import SHARED_DOCKER_CONFIG
|
|
17
|
+
from dagster_cloud.workspace.config_schema.ecs import (
|
|
18
18
|
ECS_CONTAINER_CONTEXT_CONFIG as ECS_CONTAINER_CONTEXT_CONFIG,
|
|
19
19
|
SHARED_ECS_CONFIG as SHARED_ECS_CONFIG,
|
|
20
20
|
)
|
|
21
|
-
from .kubernetes import SHARED_K8S_CONFIG
|
|
21
|
+
from dagster_cloud.workspace.config_schema.kubernetes import SHARED_K8S_CONFIG
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def validate_workspace_location(workspace_location) -> Optional[
|
|
24
|
+
def validate_workspace_location(workspace_location) -> Optional[list[str]]:
|
|
25
25
|
"""Processes a single workspace location config. Returns a list of error
|
|
26
26
|
messages if any.
|
|
27
27
|
"""
|
|
@@ -29,7 +29,7 @@ def validate_workspace_location(workspace_location) -> Optional[List[str]]:
|
|
|
29
29
|
return [error.message for error in validation.errors or []]
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def validate_workspace_config(workspace_config) -> Optional[
|
|
32
|
+
def validate_workspace_config(workspace_config) -> Optional[list[str]]:
|
|
33
33
|
"""Processes an entire workspace location config. Returns a list of
|
|
34
34
|
error messages, if any.
|
|
35
35
|
"""
|
|
@@ -37,7 +37,7 @@ def validate_workspace_config(workspace_config) -> Optional[List[str]]:
|
|
|
37
37
|
return [error.message for error in validation.errors or []]
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def process_workspace_config(workspace_config) ->
|
|
40
|
+
def process_workspace_config(workspace_config) -> dict[str, Any]:
|
|
41
41
|
"""Checks a workspace config, erroring if any mismatches with config
|
|
42
42
|
and migrating an input in the legacy workspace config format to the
|
|
43
43
|
modern format, returning the validated input.
|
|
@@ -62,8 +62,21 @@ def process_workspace_config(workspace_config) -> Dict[str, Any]:
|
|
|
62
62
|
python_file = config.get("python_file")
|
|
63
63
|
package_name = config.get("package_name")
|
|
64
64
|
module_name = config.get("module_name")
|
|
65
|
+
autoload_defs_module_name = config.get("autoload_defs_module_name")
|
|
65
66
|
check.invariant(
|
|
66
|
-
len(
|
|
67
|
+
len(
|
|
68
|
+
[
|
|
69
|
+
val
|
|
70
|
+
for val in [
|
|
71
|
+
python_file,
|
|
72
|
+
package_name,
|
|
73
|
+
module_name,
|
|
74
|
+
autoload_defs_module_name,
|
|
75
|
+
]
|
|
76
|
+
if val
|
|
77
|
+
]
|
|
78
|
+
)
|
|
79
|
+
== 1,
|
|
67
80
|
"Must supply exactly one of a file name, a package name, or a module name",
|
|
68
81
|
)
|
|
69
82
|
|
|
@@ -73,7 +86,8 @@ def process_workspace_config(workspace_config) -> Dict[str, Any]:
|
|
|
73
86
|
new_location = {
|
|
74
87
|
k: v
|
|
75
88
|
for k, v in location.items()
|
|
76
|
-
if k
|
|
89
|
+
if k
|
|
90
|
+
not in ("python_file", "package_name", "module_name", "autoload_defs_module_name")
|
|
77
91
|
}
|
|
78
92
|
new_location["code_source"] = {}
|
|
79
93
|
if "python_file" in location:
|
|
@@ -82,6 +96,10 @@ def process_workspace_config(workspace_config) -> Dict[str, Any]:
|
|
|
82
96
|
new_location["code_source"]["package_name"] = location["package_name"]
|
|
83
97
|
elif "module_name" in location:
|
|
84
98
|
new_location["code_source"]["module_name"] = location["module_name"]
|
|
99
|
+
elif "autoload_defs_module_name" in location:
|
|
100
|
+
new_location["code_source"]["autoload_defs_module_name"] = location[
|
|
101
|
+
"autoload_defs_module_name"
|
|
102
|
+
]
|
|
85
103
|
|
|
86
104
|
new_location["location_name"] = name
|
|
87
105
|
updated_locations.append(new_location)
|
|
@@ -90,10 +108,12 @@ def process_workspace_config(workspace_config) -> Dict[str, Any]:
|
|
|
90
108
|
check.is_list(workspace_config.get("locations"))
|
|
91
109
|
|
|
92
110
|
validation = validate_config(WORKSPACE_CONFIG_SCHEMA, workspace_config)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
if not validation.success:
|
|
112
|
+
check.failed(
|
|
113
|
+
", ".join(
|
|
114
|
+
[error.message for error in cast("list[EvaluationError]", validation.errors)]
|
|
115
|
+
),
|
|
116
|
+
)
|
|
97
117
|
return workspace_config
|
|
98
118
|
|
|
99
119
|
|
|
@@ -139,6 +159,8 @@ K8S_CONFIG_FIELDS = {
|
|
|
139
159
|
),
|
|
140
160
|
is_required=False,
|
|
141
161
|
),
|
|
162
|
+
"deployment_metadata": Permissive(),
|
|
163
|
+
"service_metadata": Permissive(),
|
|
142
164
|
}
|
|
143
165
|
),
|
|
144
166
|
is_required=False,
|
|
@@ -190,6 +212,10 @@ CONFIG_SCHEMA_FIELDS = {
|
|
|
190
212
|
config=str,
|
|
191
213
|
description="Python module containing the target Dagster repository.",
|
|
192
214
|
),
|
|
215
|
+
"autoload_defs_module_name": Field(
|
|
216
|
+
config=str,
|
|
217
|
+
description="Python module to automatically load Dagster definitions from.",
|
|
218
|
+
),
|
|
193
219
|
},
|
|
194
220
|
),
|
|
195
221
|
description="Python entry point for the code location.",
|
|
@@ -250,6 +276,17 @@ CONFIG_SCHEMA_FIELDS = {
|
|
|
250
276
|
is_required=False,
|
|
251
277
|
description="Locations that specify an agent queue will only have their requests handled by agents configured to read from a matching queue. By default, requests are placed on a default queue that's handled by all agents.",
|
|
252
278
|
),
|
|
279
|
+
"defs_state_info": Field(
|
|
280
|
+
config=Shape(
|
|
281
|
+
fields={
|
|
282
|
+
"info_mapping": Map(
|
|
283
|
+
str, Noneable(Shape(fields={"version": str, "create_timestamp": float}))
|
|
284
|
+
)
|
|
285
|
+
},
|
|
286
|
+
),
|
|
287
|
+
is_required=False,
|
|
288
|
+
description="Defs state info for the code location.",
|
|
289
|
+
),
|
|
253
290
|
}
|
|
254
291
|
|
|
255
292
|
|
|
@@ -278,6 +315,11 @@ LEGACY_CONFIG_SCHEMA_FIELDS = {
|
|
|
278
315
|
is_required=False,
|
|
279
316
|
description="Python module containing the target Dagster repository.",
|
|
280
317
|
),
|
|
318
|
+
"autoload_defs_module_name": Field(
|
|
319
|
+
config=str,
|
|
320
|
+
is_required=False,
|
|
321
|
+
description="Python module to automatically load Dagster definitions from.",
|
|
322
|
+
),
|
|
281
323
|
}
|
|
282
324
|
LEGACY_LOCATION_CONFIG_SCHEMA = Shape(fields=LEGACY_CONFIG_SCHEMA_FIELDS)
|
|
283
325
|
LEGACY_NAMED_LOCATIONS_CONFIG_SCHEMA = Map(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, NamedTuple, Optional
|
|
5
5
|
|
|
6
6
|
import docker
|
|
7
7
|
import docker.errors
|
|
@@ -10,36 +10,44 @@ from dagster import (
|
|
|
10
10
|
IntSource,
|
|
11
11
|
_check as check,
|
|
12
12
|
)
|
|
13
|
+
from dagster._core.launcher.base import LaunchRunContext
|
|
13
14
|
from dagster._core.utils import parse_env_var
|
|
15
|
+
from dagster._grpc.types import ExecuteRunArgs
|
|
14
16
|
from dagster._serdes import ConfigurableClass
|
|
15
17
|
from dagster._serdes.config_class import ConfigurableClassData
|
|
16
18
|
from dagster._utils import find_free_port
|
|
17
19
|
from dagster._utils.merger import merge_dicts
|
|
18
20
|
from dagster._vendored.dateutil.parser import parse
|
|
21
|
+
from dagster_cloud_cli.core.workspace import PexMetadata
|
|
19
22
|
from dagster_docker import DockerRunLauncher
|
|
20
23
|
from dagster_docker.container_context import DockerContainerContext
|
|
24
|
+
from dagster_shared.serdes.serdes import deserialize_value, serialize_value
|
|
21
25
|
from docker.models.containers import Container
|
|
22
26
|
from typing_extensions import Self
|
|
23
27
|
|
|
24
28
|
from dagster_cloud.api.dagster_cloud_api import UserCodeDeploymentType
|
|
25
29
|
from dagster_cloud.execution.monitoring import CloudContainerResourceLimits
|
|
26
|
-
from dagster_cloud.
|
|
27
|
-
|
|
28
|
-
from
|
|
29
|
-
from
|
|
30
|
+
from dagster_cloud.storage.tags import PEX_METADATA_TAG
|
|
31
|
+
from dagster_cloud.workspace.config_schema.docker import SHARED_DOCKER_CONFIG
|
|
32
|
+
from dagster_cloud.workspace.docker.utils import unique_docker_resource_name
|
|
33
|
+
from dagster_cloud.workspace.user_code_launcher import (
|
|
30
34
|
DEFAULT_SERVER_PROCESS_STARTUP_TIMEOUT,
|
|
31
35
|
SHARED_USER_CODE_LAUNCHER_CONFIG,
|
|
32
36
|
DagsterCloudGrpcServer,
|
|
33
37
|
DagsterCloudUserCodeLauncher,
|
|
34
38
|
ServerEndpoint,
|
|
35
39
|
)
|
|
36
|
-
from
|
|
37
|
-
from .utils import
|
|
40
|
+
from dagster_cloud.workspace.user_code_launcher.user_code_launcher import UserCodeLauncherEntry
|
|
41
|
+
from dagster_cloud.workspace.user_code_launcher.utils import (
|
|
42
|
+
deterministic_label_for_location,
|
|
43
|
+
get_grpc_server_env,
|
|
44
|
+
)
|
|
38
45
|
|
|
39
46
|
GRPC_SERVER_LABEL = "dagster_grpc_server"
|
|
40
47
|
MULTIPEX_SERVER_LABEL = "dagster_multipex_server"
|
|
41
48
|
AGENT_LABEL = "dagster_agent_id"
|
|
42
49
|
SERVER_TIMESTAMP_LABEL = "dagster_server_timestamp"
|
|
50
|
+
STOP_TIMEOUT_LABEL = "dagster_stop_timeout"
|
|
43
51
|
|
|
44
52
|
|
|
45
53
|
IMAGE_PULL_LOG_INTERVAL = 15
|
|
@@ -52,7 +60,7 @@ class DagsterDockerContainer(NamedTuple):
|
|
|
52
60
|
|
|
53
61
|
container: Container
|
|
54
62
|
|
|
55
|
-
def __str__(self):
|
|
63
|
+
def __str__(self): # pyright: ignore[reportIncompatibleMethodOverride]
|
|
56
64
|
return self.container.id
|
|
57
65
|
|
|
58
66
|
|
|
@@ -76,7 +84,7 @@ class DockerUserCodeLauncher(
|
|
|
76
84
|
container_kwargs, "container_kwargs", key_type=str
|
|
77
85
|
)
|
|
78
86
|
|
|
79
|
-
super(
|
|
87
|
+
super().__init__(**kwargs)
|
|
80
88
|
|
|
81
89
|
@property
|
|
82
90
|
def requires_images(self):
|
|
@@ -90,11 +98,11 @@ class DockerUserCodeLauncher(
|
|
|
90
98
|
return self._inst_data
|
|
91
99
|
|
|
92
100
|
@property
|
|
93
|
-
def env_vars(self) ->
|
|
101
|
+
def env_vars(self) -> list[str]:
|
|
94
102
|
return self._input_env_vars + self._instance.dagster_cloud_api_env_vars
|
|
95
103
|
|
|
96
104
|
@property
|
|
97
|
-
def container_kwargs(self) ->
|
|
105
|
+
def container_kwargs(self) -> dict[str, Any]:
|
|
98
106
|
return self._container_kwargs
|
|
99
107
|
|
|
100
108
|
@classmethod
|
|
@@ -130,6 +138,10 @@ class DockerUserCodeLauncher(
|
|
|
130
138
|
command,
|
|
131
139
|
labels,
|
|
132
140
|
):
|
|
141
|
+
container_kwargs = {**container_context.container_kwargs}
|
|
142
|
+
|
|
143
|
+
container_kwargs.pop("stop_timeout", None)
|
|
144
|
+
|
|
133
145
|
return client.containers.create(
|
|
134
146
|
image,
|
|
135
147
|
detach=True,
|
|
@@ -140,12 +152,12 @@ class DockerUserCodeLauncher(
|
|
|
140
152
|
labels=labels,
|
|
141
153
|
command=command,
|
|
142
154
|
ports=ports,
|
|
143
|
-
**
|
|
155
|
+
**container_kwargs,
|
|
144
156
|
)
|
|
145
157
|
|
|
146
158
|
def _get_standalone_dagster_server_handles_for_location(
|
|
147
159
|
self, deployment_name: str, location_name: str
|
|
148
|
-
) ->
|
|
160
|
+
) -> list[DagsterDockerContainer]:
|
|
149
161
|
client = docker.client.from_env()
|
|
150
162
|
return [
|
|
151
163
|
DagsterDockerContainer(container=container)
|
|
@@ -163,7 +175,7 @@ class DockerUserCodeLauncher(
|
|
|
163
175
|
|
|
164
176
|
def _get_multipex_server_handles_for_location(
|
|
165
177
|
self, deployment_name: str, location_name: str
|
|
166
|
-
) ->
|
|
178
|
+
) -> list[DagsterDockerContainer]:
|
|
167
179
|
client = docker.client.from_env()
|
|
168
180
|
return [
|
|
169
181
|
DagsterDockerContainer(container=container)
|
|
@@ -186,13 +198,13 @@ class DockerUserCodeLauncher(
|
|
|
186
198
|
container_name: str,
|
|
187
199
|
hostname: str,
|
|
188
200
|
grpc_port: int,
|
|
189
|
-
ports:
|
|
201
|
+
ports: dict[int, int],
|
|
190
202
|
image: str,
|
|
191
203
|
container_context: DockerContainerContext,
|
|
192
|
-
command:
|
|
193
|
-
additional_env:
|
|
194
|
-
labels:
|
|
195
|
-
) ->
|
|
204
|
+
command: list[str],
|
|
205
|
+
additional_env: dict[str, str],
|
|
206
|
+
labels: dict[str, str],
|
|
207
|
+
) -> tuple[Container, ServerEndpoint]:
|
|
196
208
|
client = docker.client.from_env()
|
|
197
209
|
|
|
198
210
|
self._logger.info(
|
|
@@ -304,8 +316,11 @@ class DockerUserCodeLauncher(
|
|
|
304
316
|
command = metadata.get_grpc_server_command(
|
|
305
317
|
metrics_enabled=self._instance.user_code_launcher.code_server_metrics_enabled
|
|
306
318
|
)
|
|
307
|
-
environment =
|
|
308
|
-
|
|
319
|
+
environment = get_grpc_server_env(
|
|
320
|
+
metadata,
|
|
321
|
+
grpc_port,
|
|
322
|
+
location_name,
|
|
323
|
+
self._instance.ref_for_deployment(deployment_name),
|
|
309
324
|
)
|
|
310
325
|
labels = {
|
|
311
326
|
GRPC_SERVER_LABEL: "",
|
|
@@ -314,6 +329,9 @@ class DockerUserCodeLauncher(
|
|
|
314
329
|
SERVER_TIMESTAMP_LABEL: str(desired_entry.update_timestamp),
|
|
315
330
|
}
|
|
316
331
|
|
|
332
|
+
if "stop_timeout" in container_context.container_kwargs:
|
|
333
|
+
labels[STOP_TIMEOUT_LABEL] = str(container_context.container_kwargs["stop_timeout"])
|
|
334
|
+
|
|
317
335
|
container, server_endpoint = self._launch_container(
|
|
318
336
|
deployment_name,
|
|
319
337
|
location_name,
|
|
@@ -347,8 +365,12 @@ class DockerUserCodeLauncher(
|
|
|
347
365
|
|
|
348
366
|
def _remove_server_handle(self, server_handle: DagsterDockerContainer) -> None:
|
|
349
367
|
container = server_handle.container
|
|
368
|
+
|
|
369
|
+
stop_timeout_str = server_handle.container.labels.get(STOP_TIMEOUT_LABEL)
|
|
370
|
+
stop_timeout = int(stop_timeout_str) if stop_timeout_str else None
|
|
371
|
+
|
|
350
372
|
try:
|
|
351
|
-
container.stop()
|
|
373
|
+
container.stop(timeout=stop_timeout)
|
|
352
374
|
except Exception:
|
|
353
375
|
self._logger.error(f"Failure stopping container {container.id}: {sys.exc_info()}")
|
|
354
376
|
container.remove(force=True)
|
|
@@ -359,9 +381,9 @@ class DockerUserCodeLauncher(
|
|
|
359
381
|
|
|
360
382
|
def get_server_create_timestamp(self, handle: DagsterDockerContainer) -> Optional[float]:
|
|
361
383
|
created_time_str = handle.container.attrs["Created"]
|
|
362
|
-
return parse(created_time_str).timestamp()
|
|
384
|
+
return parse(created_time_str).timestamp() # pyright: ignore[reportAttributeAccessIssue]
|
|
363
385
|
|
|
364
|
-
def _list_server_handles(self) ->
|
|
386
|
+
def _list_server_handles(self) -> list[DagsterDockerContainer]:
|
|
365
387
|
client = docker.client.from_env()
|
|
366
388
|
return [
|
|
367
389
|
DagsterDockerContainer(container=container)
|
|
@@ -374,7 +396,7 @@ class DockerUserCodeLauncher(
|
|
|
374
396
|
]
|
|
375
397
|
|
|
376
398
|
def run_launcher(self):
|
|
377
|
-
launcher =
|
|
399
|
+
launcher = CloudDockerRunLauncher(
|
|
378
400
|
image=None,
|
|
379
401
|
env_vars=self.env_vars,
|
|
380
402
|
networks=self._networks,
|
|
@@ -383,3 +405,32 @@ class DockerUserCodeLauncher(
|
|
|
383
405
|
launcher.register_instance(self._instance)
|
|
384
406
|
|
|
385
407
|
return launcher
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class CloudDockerRunLauncher(DockerRunLauncher):
|
|
411
|
+
def launch_run(self, context: LaunchRunContext) -> None:
|
|
412
|
+
serialized_pex_metadata = context.dagster_run.tags.get(PEX_METADATA_TAG)
|
|
413
|
+
|
|
414
|
+
if serialized_pex_metadata:
|
|
415
|
+
run = context.dagster_run
|
|
416
|
+
job_origin = check.not_none(run.job_code_origin)
|
|
417
|
+
|
|
418
|
+
docker_image = self._get_docker_image(job_origin)
|
|
419
|
+
|
|
420
|
+
run_args = ExecuteRunArgs(
|
|
421
|
+
job_origin=job_origin,
|
|
422
|
+
run_id=run.run_id,
|
|
423
|
+
instance_ref=self._instance.get_ref(),
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
deserialize_value(serialized_pex_metadata, PexMetadata)
|
|
427
|
+
command = [
|
|
428
|
+
"dagster-cloud",
|
|
429
|
+
"pex",
|
|
430
|
+
"execute-run",
|
|
431
|
+
serialize_value(run_args),
|
|
432
|
+
serialized_pex_metadata,
|
|
433
|
+
]
|
|
434
|
+
self._launch_container_with_command(run, docker_image, command)
|
|
435
|
+
else:
|
|
436
|
+
return super().launch_run(context)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
from .launcher import EcsUserCodeLauncher as EcsUserCodeLauncher
|
|
1
|
+
from dagster_cloud.workspace.ecs.launcher import EcsUserCodeLauncher as EcsUserCodeLauncher
|