dagster-cloud 1.10.11__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/dagster_cloud_agent.py +64 -20
- dagster_cloud/agent/instrumentation/run_launch.py +2 -2
- dagster_cloud/agent/instrumentation/schedule.py +1 -1
- dagster_cloud/agent/instrumentation/sensor.py +1 -1
- dagster_cloud/anomaly_detection/__init__.py +2 -2
- dagster_cloud/anomaly_detection/defs.py +11 -8
- dagster_cloud/api/dagster_cloud_api.py +7 -5
- dagster_cloud/auth/constants.py +21 -5
- dagster_cloud/batching/__init__.py +1 -1
- dagster_cloud/dagster_insights/__init__.py +12 -6
- dagster_cloud/dagster_insights/bigquery/dbt_wrapper.py +8 -2
- dagster_cloud/dagster_insights/bigquery/insights_bigquery_resource.py +4 -2
- dagster_cloud/dagster_insights/insights_utils.py +1 -1
- dagster_cloud/dagster_insights/metrics_utils.py +1 -1
- dagster_cloud/dagster_insights/snowflake/dagster_snowflake_insights.py +1 -9
- dagster_cloud/dagster_insights/snowflake/dbt_wrapper.py +9 -2
- dagster_cloud/dagster_insights/snowflake/definitions.py +5 -4
- dagster_cloud/dagster_insights/snowflake/insights_snowflake_resource.py +1 -2
- dagster_cloud/definitions/__init__.py +0 -0
- dagster_cloud/definitions/job_selection.py +36 -0
- dagster_cloud/execution/utils/process.py +1 -1
- dagster_cloud/instance/__init__.py +81 -42
- dagster_cloud/metadata/source_code.py +3 -1
- dagster_cloud/opentelemetry/config/exporter.py +1 -1
- dagster_cloud/opentelemetry/controller.py +1 -1
- dagster_cloud/opentelemetry/observers/dagster_exception_handler.py +1 -1
- dagster_cloud/opentelemetry/observers/execution_observer.py +4 -2
- dagster_cloud/pex/grpc/__init__.py +2 -2
- dagster_cloud/pex/grpc/client.py +2 -2
- dagster_cloud/pex/grpc/server/__init__.py +2 -2
- dagster_cloud/pex/grpc/server/cli/__init__.py +2 -2
- dagster_cloud/pex/grpc/server/manager.py +5 -4
- dagster_cloud/pex/grpc/server/registry.py +15 -8
- dagster_cloud/pex/grpc/server/server.py +17 -8
- dagster_cloud/secrets/__init__.py +1 -1
- dagster_cloud/serverless/__init__.py +1 -1
- dagster_cloud/serverless/io_manager.py +4 -1
- dagster_cloud/storage/compute_logs/__init__.py +3 -1
- dagster_cloud/storage/compute_logs/compute_log_manager.py +17 -13
- 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/storage.py +9 -2
- dagster_cloud/storage/event_logs/utils.py +1 -3
- dagster_cloud/storage/runs/__init__.py +1 -1
- dagster_cloud/storage/runs/queries.py +15 -0
- dagster_cloud/storage/runs/storage.py +30 -3
- dagster_cloud/storage/schedules/__init__.py +1 -1
- dagster_cloud/storage/schedules/storage.py +1 -1
- dagster_cloud/util/errors.py +0 -91
- dagster_cloud/version.py +1 -1
- dagster_cloud/workspace/config_schema/__init__.py +43 -5
- dagster_cloud/workspace/docker/__init__.py +8 -7
- dagster_cloud/workspace/docker/utils.py +1 -1
- dagster_cloud/workspace/ecs/__init__.py +1 -1
- dagster_cloud/workspace/ecs/client.py +23 -18
- dagster_cloud/workspace/ecs/launcher.py +19 -5
- dagster_cloud/workspace/ecs/run_launcher.py +1 -2
- dagster_cloud/workspace/ecs/utils.py +5 -2
- dagster_cloud/workspace/kubernetes/__init__.py +1 -1
- dagster_cloud/workspace/kubernetes/launcher.py +11 -12
- dagster_cloud/workspace/kubernetes/utils.py +1 -2
- dagster_cloud/workspace/user_code_launcher/__init__.py +5 -3
- dagster_cloud/workspace/user_code_launcher/process.py +2 -3
- dagster_cloud/workspace/user_code_launcher/user_code_launcher.py +71 -34
- dagster_cloud/workspace/user_code_launcher/utils.py +7 -0
- {dagster_cloud-1.10.11.dist-info → dagster_cloud-1.12.6.dist-info}/METADATA +9 -8
- dagster_cloud-1.12.6.dist-info/RECORD +134 -0
- {dagster_cloud-1.10.11.dist-info → dagster_cloud-1.12.6.dist-info}/WHEEL +1 -1
- dagster_cloud-1.10.11.dist-info/RECORD +0 -129
- {dagster_cloud-1.10.11.dist-info → dagster_cloud-1.12.6.dist-info}/top_level.txt +0 -0
|
@@ -20,6 +20,7 @@ from dagster._core.instance.config import config_field_for_configurable_class
|
|
|
20
20
|
from dagster._core.instance.ref import InstanceRef, configurable_class_data
|
|
21
21
|
from dagster._core.launcher import DefaultRunLauncher, RunLauncher
|
|
22
22
|
from dagster._core.storage.dagster_run import DagsterRun
|
|
23
|
+
from dagster._core.storage.defs_state.base import DefsStateStorage
|
|
23
24
|
from dagster._serdes import ConfigurableClassData
|
|
24
25
|
from dagster_cloud_cli.core.graphql_client import (
|
|
25
26
|
create_agent_graphql_client,
|
|
@@ -31,14 +32,13 @@ from dagster_cloud_cli.core.headers.auth import DagsterCloudInstanceScope
|
|
|
31
32
|
from urllib3 import Retry
|
|
32
33
|
|
|
33
34
|
from dagster_cloud.agent import AgentQueuesConfig
|
|
35
|
+
from dagster_cloud.auth.constants import decode_agent_token
|
|
36
|
+
from dagster_cloud.opentelemetry.config import opentelemetry_config_schema
|
|
37
|
+
from dagster_cloud.opentelemetry.controller import OpenTelemetryController
|
|
38
|
+
from dagster_cloud.storage.client import dagster_cloud_api_config
|
|
39
|
+
from dagster_cloud.util import get_env_names_from_config, is_isolated_run
|
|
34
40
|
from dagster_cloud.version import __version__
|
|
35
41
|
|
|
36
|
-
from ..auth.constants import get_organization_name_from_agent_token
|
|
37
|
-
from ..opentelemetry.config import opentelemetry_config_schema
|
|
38
|
-
from ..opentelemetry.controller import OpenTelemetryController
|
|
39
|
-
from ..storage.client import dagster_cloud_api_config
|
|
40
|
-
from ..util import get_env_names_from_config, is_isolated_run
|
|
41
|
-
|
|
42
42
|
if TYPE_CHECKING:
|
|
43
43
|
from requests import Session
|
|
44
44
|
|
|
@@ -64,6 +64,11 @@ class DagsterCloudInstance(DagsterInstance):
|
|
|
64
64
|
"run_retries.retry_on_asset_or_op_failure is a deployment setting and can only be accessed by a DeploymentScopedHostInstance"
|
|
65
65
|
)
|
|
66
66
|
|
|
67
|
+
@property
|
|
68
|
+
def defs_state_storage(self) -> Optional[DefsStateStorage]:
|
|
69
|
+
# only DeploymentScopedHostInstance / DagsterCloudAgentInstance have a defs state storage
|
|
70
|
+
return None
|
|
71
|
+
|
|
67
72
|
|
|
68
73
|
class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
69
74
|
def __init__(
|
|
@@ -81,14 +86,18 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
81
86
|
super().__init__(*args, **kwargs)
|
|
82
87
|
|
|
83
88
|
self._unprocessed_dagster_cloud_api_config = dagster_cloud_api
|
|
84
|
-
self._dagster_cloud_api_config =
|
|
85
|
-
|
|
89
|
+
self._dagster_cloud_api_config = check.not_none(
|
|
90
|
+
self._get_processed_config(
|
|
91
|
+
"dagster_cloud_api",
|
|
92
|
+
dagster_cloud_api,
|
|
93
|
+
dagster_cloud_api_config(),
|
|
94
|
+
)
|
|
86
95
|
)
|
|
87
96
|
|
|
88
97
|
check.invariant(
|
|
89
98
|
not (
|
|
90
|
-
self._dagster_cloud_api_config.get("deployment")
|
|
91
|
-
and self._dagster_cloud_api_config.get("deployments")
|
|
99
|
+
self._dagster_cloud_api_config.get("deployment")
|
|
100
|
+
and self._dagster_cloud_api_config.get("deployments")
|
|
92
101
|
),
|
|
93
102
|
"Cannot set both deployment and deployments in `dagster_cloud_api`",
|
|
94
103
|
)
|
|
@@ -144,6 +153,17 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
144
153
|
|
|
145
154
|
self._instance_uuid = str(uuid.uuid4())
|
|
146
155
|
|
|
156
|
+
@property
|
|
157
|
+
def defs_state_storage(self) -> Optional[DefsStateStorage]:
|
|
158
|
+
# temporary hack to avoid cases where the default BlobStorageStateStorage is used
|
|
159
|
+
from dagster_cloud.storage.defs_state.storage import GraphQLDefsStateStorage
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
self._defs_state_storage
|
|
163
|
+
if isinstance(self._defs_state_storage, GraphQLDefsStateStorage)
|
|
164
|
+
else None
|
|
165
|
+
)
|
|
166
|
+
|
|
147
167
|
def _get_processed_config(
|
|
148
168
|
self, name: str, config: Optional[dict[str, Any]], config_type: dict[str, Any]
|
|
149
169
|
):
|
|
@@ -158,16 +178,16 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
158
178
|
return processed_config.value
|
|
159
179
|
|
|
160
180
|
def _dagster_cloud_api_config_for_deployment(self, deployment_name: Optional[str]):
|
|
161
|
-
new_api_config = dict(copy.deepcopy(self._dagster_cloud_api_config))
|
|
181
|
+
new_api_config = dict(copy.deepcopy(self._dagster_cloud_api_config))
|
|
162
182
|
if deployment_name:
|
|
163
|
-
new_api_config["deployment"] = deployment_name
|
|
183
|
+
new_api_config["deployment"] = deployment_name
|
|
164
184
|
if self.includes_branch_deployments:
|
|
165
|
-
del new_api_config["branch_deployments"]
|
|
166
|
-
new_api_config.pop("deployments", None)
|
|
167
|
-
new_api_config.pop("all_serverless_deployments", None)
|
|
185
|
+
del new_api_config["branch_deployments"]
|
|
186
|
+
new_api_config.pop("deployments", None)
|
|
187
|
+
new_api_config.pop("all_serverless_deployments", None)
|
|
168
188
|
else:
|
|
169
|
-
new_api_config.pop("deployment", None)
|
|
170
|
-
new_api_config.pop("deployments", None)
|
|
189
|
+
new_api_config.pop("deployment", None)
|
|
190
|
+
new_api_config.pop("deployments", None)
|
|
171
191
|
|
|
172
192
|
return new_api_config
|
|
173
193
|
|
|
@@ -182,7 +202,7 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
182
202
|
return create_agent_graphql_client(
|
|
183
203
|
self.client_managed_retries_requests_session,
|
|
184
204
|
self.dagster_cloud_graphql_url,
|
|
185
|
-
self._dagster_cloud_api_config_for_deployment(None),
|
|
205
|
+
self._dagster_cloud_api_config_for_deployment(None),
|
|
186
206
|
scope=DagsterCloudInstanceScope.ORGANIZATION,
|
|
187
207
|
)
|
|
188
208
|
|
|
@@ -190,13 +210,13 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
190
210
|
return create_agent_graphql_client(
|
|
191
211
|
self.client_managed_retries_requests_session,
|
|
192
212
|
self.dagster_cloud_graphql_url,
|
|
193
|
-
self._dagster_cloud_api_config_for_deployment(deployment_name),
|
|
213
|
+
self._dagster_cloud_api_config_for_deployment(deployment_name),
|
|
194
214
|
scope=DagsterCloudInstanceScope.DEPLOYMENT,
|
|
195
215
|
)
|
|
196
216
|
|
|
197
217
|
def headers_for_deployment(self, deployment_name: str):
|
|
198
218
|
return get_agent_headers(
|
|
199
|
-
self._dagster_cloud_api_config_for_deployment(deployment_name),
|
|
219
|
+
self._dagster_cloud_api_config_for_deployment(deployment_name),
|
|
200
220
|
DagsterCloudInstanceScope.DEPLOYMENT,
|
|
201
221
|
)
|
|
202
222
|
|
|
@@ -221,11 +241,11 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
221
241
|
return socket_option
|
|
222
242
|
|
|
223
243
|
def _socket_options(self):
|
|
224
|
-
if self._dagster_cloud_api_config.get("socket_options") is None:
|
|
244
|
+
if self._dagster_cloud_api_config.get("socket_options") is None:
|
|
225
245
|
return None
|
|
226
246
|
|
|
227
247
|
translated_socket_options = []
|
|
228
|
-
for socket_option in self._dagster_cloud_api_config["socket_options"]:
|
|
248
|
+
for socket_option in self._dagster_cloud_api_config["socket_options"]:
|
|
229
249
|
check.invariant(
|
|
230
250
|
len(socket_option) == 3, "Each socket option must be a list of three values"
|
|
231
251
|
)
|
|
@@ -267,7 +287,7 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
267
287
|
adapter_kwargs=dict(
|
|
268
288
|
max_retries=Retry(
|
|
269
289
|
total=self.dagster_cloud_api_retries,
|
|
270
|
-
backoff_factor=self._dagster_cloud_api_config["backoff_factor"],
|
|
290
|
+
backoff_factor=self._dagster_cloud_api_config["backoff_factor"],
|
|
271
291
|
),
|
|
272
292
|
socket_options=self._socket_options(),
|
|
273
293
|
)
|
|
@@ -297,21 +317,26 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
297
317
|
|
|
298
318
|
@property
|
|
299
319
|
def dagster_cloud_url(self):
|
|
300
|
-
if "url" in self._dagster_cloud_api_config:
|
|
301
|
-
return self._dagster_cloud_api_config["url"]
|
|
320
|
+
if "url" in self._dagster_cloud_api_config:
|
|
321
|
+
return self._dagster_cloud_api_config["url"]
|
|
302
322
|
|
|
303
|
-
organization =
|
|
323
|
+
organization, region = decode_agent_token(self.dagster_cloud_agent_token)
|
|
304
324
|
if not organization:
|
|
305
325
|
raise DagsterInvariantViolationError(
|
|
306
326
|
"Could not derive Dagster Cloud URL from agent token. Create a new agent token or"
|
|
307
327
|
" set the `url` field under `dagster_cloud_api` in your `dagster.yaml`."
|
|
308
328
|
)
|
|
309
329
|
|
|
310
|
-
return
|
|
330
|
+
return (
|
|
331
|
+
f"https://{organization}.agent.{region}.dagster.cloud"
|
|
332
|
+
if region
|
|
333
|
+
else f"https://{organization}.agent.dagster.cloud"
|
|
334
|
+
)
|
|
311
335
|
|
|
312
336
|
@property
|
|
313
337
|
def organization_name(self) -> Optional[str]:
|
|
314
|
-
|
|
338
|
+
organization, _ = decode_agent_token(self.dagster_cloud_agent_token)
|
|
339
|
+
return organization
|
|
315
340
|
|
|
316
341
|
@property
|
|
317
342
|
def deployment_name(self) -> Optional[str]:
|
|
@@ -326,26 +351,31 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
326
351
|
|
|
327
352
|
@property
|
|
328
353
|
def deployment_names(self) -> list[str]:
|
|
329
|
-
if self._dagster_cloud_api_config.get("deployment"):
|
|
330
|
-
return [self._dagster_cloud_api_config["deployment"]]
|
|
354
|
+
if self._dagster_cloud_api_config.get("deployment"):
|
|
355
|
+
return [self._dagster_cloud_api_config["deployment"]]
|
|
331
356
|
|
|
332
|
-
return self._dagster_cloud_api_config.get("deployments", [])
|
|
357
|
+
return self._dagster_cloud_api_config.get("deployments", [])
|
|
333
358
|
|
|
334
359
|
@property
|
|
335
360
|
def include_all_serverless_deployments(self) -> bool:
|
|
336
|
-
return self._dagster_cloud_api_config.get("all_serverless_deployments")
|
|
361
|
+
return self._dagster_cloud_api_config.get("all_serverless_deployments") or False
|
|
337
362
|
|
|
338
363
|
@property
|
|
339
364
|
def dagit_url(self):
|
|
340
|
-
organization =
|
|
365
|
+
organization, region = decode_agent_token(self.dagster_cloud_agent_token)
|
|
341
366
|
if not organization:
|
|
342
367
|
raise Exception(
|
|
343
368
|
"Could not derive Dagster Cloud URL from agent token to generate a Dagit URL."
|
|
344
369
|
" Generate a new agent token in the Dagit UI."
|
|
345
370
|
)
|
|
346
371
|
|
|
347
|
-
deployment = self._dagster_cloud_api_config.get("deployment")
|
|
348
|
-
|
|
372
|
+
deployment = self._dagster_cloud_api_config.get("deployment")
|
|
373
|
+
base_url = (
|
|
374
|
+
f"https://{organization}.{region}.dagster.cloud/"
|
|
375
|
+
if region
|
|
376
|
+
else f"https://{organization}.dagster.cloud/"
|
|
377
|
+
)
|
|
378
|
+
return base_url + (f"{deployment}/" if deployment else "")
|
|
349
379
|
|
|
350
380
|
@property
|
|
351
381
|
def dagster_cloud_graphql_url(self):
|
|
@@ -395,33 +425,38 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
395
425
|
return get_agent_headers(self._dagster_cloud_api_config, scope=scope) # pyright: ignore[reportArgumentType]
|
|
396
426
|
|
|
397
427
|
@property
|
|
398
|
-
def dagster_cloud_agent_token(self):
|
|
399
|
-
|
|
428
|
+
def dagster_cloud_agent_token(self) -> str:
|
|
429
|
+
check.invariant(
|
|
430
|
+
self._dagster_cloud_api_config.get("agent_token") is not None,
|
|
431
|
+
"No agent token found in dagster_cloud_api configuration. An agent token is required"
|
|
432
|
+
" for Dagster Cloud authentication.",
|
|
433
|
+
)
|
|
434
|
+
return self._dagster_cloud_api_config["agent_token"]
|
|
400
435
|
|
|
401
436
|
@property
|
|
402
437
|
def dagster_cloud_api_retries(self) -> int:
|
|
403
|
-
return self._dagster_cloud_api_config["retries"]
|
|
438
|
+
return self._dagster_cloud_api_config["retries"]
|
|
404
439
|
|
|
405
440
|
@property
|
|
406
441
|
def dagster_cloud_api_timeout(self) -> int:
|
|
407
|
-
return self._dagster_cloud_api_config["timeout"]
|
|
442
|
+
return self._dagster_cloud_api_config["timeout"]
|
|
408
443
|
|
|
409
444
|
@property
|
|
410
445
|
def dagster_cloud_api_proxies(self) -> Optional[dict[str, str]]:
|
|
411
446
|
# Requests library modifies the proxies key so create a copy
|
|
412
447
|
return (
|
|
413
448
|
self._dagster_cloud_api_config.get("proxies").copy() # pyright: ignore[reportOptionalMemberAccess]
|
|
414
|
-
if self._dagster_cloud_api_config.get("proxies")
|
|
449
|
+
if self._dagster_cloud_api_config.get("proxies")
|
|
415
450
|
else {}
|
|
416
451
|
)
|
|
417
452
|
|
|
418
453
|
@property
|
|
419
454
|
def dagster_cloud_api_agent_label(self) -> Optional[str]:
|
|
420
|
-
return self._dagster_cloud_api_config.get("agent_label")
|
|
455
|
+
return self._dagster_cloud_api_config.get("agent_label")
|
|
421
456
|
|
|
422
457
|
@property
|
|
423
458
|
def includes_branch_deployments(self) -> bool:
|
|
424
|
-
return self._dagster_cloud_api_config.get("branch_deployments", False)
|
|
459
|
+
return self._dagster_cloud_api_config.get("branch_deployments", False)
|
|
425
460
|
|
|
426
461
|
@property
|
|
427
462
|
def instance_uuid(self) -> str:
|
|
@@ -578,6 +613,10 @@ instance_class:
|
|
|
578
613
|
"dagster_cloud.secrets", "DagsterCloudSecretsLoader", empty_yaml
|
|
579
614
|
)
|
|
580
615
|
|
|
616
|
+
defaults["defs_state_storage"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
617
|
+
"dagster_cloud.storage.defs_state", "GraphQLDefsStateStorage", empty_yaml
|
|
618
|
+
)
|
|
619
|
+
|
|
581
620
|
return defaults
|
|
582
621
|
|
|
583
622
|
def dispose(self) -> None:
|
|
@@ -14,7 +14,9 @@ from dagster._core.types.loadable_target_origin import LoadableTargetOrigin
|
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from dagster import AssetsDefinition, AssetSpec, SourceAsset
|
|
17
|
-
from dagster._core.definitions.
|
|
17
|
+
from dagster._core.definitions.assets.definition.cacheable_assets_definition import (
|
|
18
|
+
CacheableAssetsDefinition,
|
|
19
|
+
)
|
|
18
20
|
|
|
19
21
|
import sys
|
|
20
22
|
|
|
@@ -251,7 +251,7 @@ class OpenTelemetryController:
|
|
|
251
251
|
)
|
|
252
252
|
self._logging_handler = logs_factory.build_logging_handler(
|
|
253
253
|
self._logger_provider, # pyright: ignore[reportArgumentType]
|
|
254
|
-
logging_config.get("handler", {}),
|
|
254
|
+
logging_config.get("handler", {}),
|
|
255
255
|
)
|
|
256
256
|
self._logger.addHandler(self._logging_handler)
|
|
257
257
|
|
|
@@ -21,7 +21,7 @@ def extract_dagster_error_attributes(error: DagsterError) -> dict[str, str]:
|
|
|
21
21
|
if type(attr) is list and all(isinstance(item, SerializableErrorInfo) for item in attr):
|
|
22
22
|
serializable_error_infos: list[SerializableErrorInfo] = getattr(error, field)
|
|
23
23
|
for serializable_error_info in serializable_error_infos:
|
|
24
|
-
if serializable_error_info.cause:
|
|
24
|
+
if serializable_error_info.cause and serializable_error_info.cause.cls_name:
|
|
25
25
|
error_attributes["cause"] = serializable_error_info.cause.cls_name
|
|
26
26
|
return error_attributes
|
|
27
27
|
|
|
@@ -5,8 +5,10 @@ from typing import Callable, Optional
|
|
|
5
5
|
from dagster import DagsterError
|
|
6
6
|
from dagster._time import get_current_timestamp
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from .dagster_exception_handler import
|
|
8
|
+
from dagster_cloud.opentelemetry.controller import OpenTelemetryController
|
|
9
|
+
from dagster_cloud.opentelemetry.observers.dagster_exception_handler import (
|
|
10
|
+
extract_dagster_error_attributes,
|
|
11
|
+
)
|
|
10
12
|
|
|
11
13
|
ResultEvaluatorCallback = Callable[..., str]
|
|
12
14
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from .client import (
|
|
1
|
+
from dagster_cloud.pex.grpc.client import (
|
|
2
2
|
MultiPexGrpcClient as MultiPexGrpcClient,
|
|
3
3
|
wait_for_grpc_server as wait_for_grpc_server,
|
|
4
4
|
)
|
|
5
|
-
from .types import (
|
|
5
|
+
from dagster_cloud.pex.grpc.types import (
|
|
6
6
|
CreatePexServerArgs as CreatePexServerArgs,
|
|
7
7
|
GetPexServersArgs as GetPexServersArgs,
|
|
8
8
|
PexServerHandle as PexServerHandle,
|
dagster_cloud/pex/grpc/client.py
CHANGED
|
@@ -12,8 +12,8 @@ from dagster._serdes import serialize_value
|
|
|
12
12
|
from dagster._utils.error import SerializableErrorInfo, serializable_error_info_from_exc_info
|
|
13
13
|
from dagster_shared.serdes.serdes import deserialize_value
|
|
14
14
|
|
|
15
|
-
from .__generated__ import MultiPexApiStub, multi_pex_api_pb2
|
|
16
|
-
from .types import (
|
|
15
|
+
from dagster_cloud.pex.grpc.__generated__ import MultiPexApiStub, multi_pex_api_pb2
|
|
16
|
+
from dagster_cloud.pex.grpc.types import (
|
|
17
17
|
CreatePexServerArgs,
|
|
18
18
|
CreatePexServerResponse,
|
|
19
19
|
GetCrashedPexServersArgs,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
from .manager import MultiPexManager as MultiPexManager
|
|
2
|
-
from .server import run_multipex_server as run_multipex_server
|
|
1
|
+
from dagster_cloud.pex.grpc.server.manager import MultiPexManager as MultiPexManager
|
|
2
|
+
from dagster_cloud.pex.grpc.server.server import run_multipex_server as run_multipex_server
|
|
@@ -9,8 +9,8 @@ from dagster._utils.interrupts import setup_interrupt_handlers
|
|
|
9
9
|
from dagster_cloud_cli.core.workspace import PexMetadata
|
|
10
10
|
from typer import Option, Typer
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from dagster_cloud.pex.grpc.server.registry import PexS3Registry
|
|
13
|
+
from dagster_cloud.pex.grpc.server.server import run_multipex_server
|
|
14
14
|
|
|
15
15
|
app = Typer(hidden=True)
|
|
16
16
|
|
|
@@ -18,11 +18,10 @@ from dagster_shared import seven
|
|
|
18
18
|
from dagster_shared.ipc import open_ipc_subprocess
|
|
19
19
|
from pydantic import BaseModel, Extra
|
|
20
20
|
|
|
21
|
+
from dagster_cloud.pex.grpc.server.registry import PexS3Registry
|
|
22
|
+
from dagster_cloud.pex.grpc.types import PexServerHandle
|
|
21
23
|
from dagster_cloud.workspace.user_code_launcher.utils import get_grpc_server_env
|
|
22
24
|
|
|
23
|
-
from ..types import PexServerHandle
|
|
24
|
-
from .registry import PexS3Registry
|
|
25
|
-
|
|
26
25
|
logger = logging.getLogger("dagster.multipex")
|
|
27
26
|
|
|
28
27
|
|
|
@@ -401,7 +400,9 @@ class MultiPexManager(AbstractContextManager):
|
|
|
401
400
|
# - ensure that resources for servers starting up or shutting down are not removed
|
|
402
401
|
# - important to do this while holding the lock to avoid race conditions
|
|
403
402
|
running_server_ids = {
|
|
404
|
-
proc.pex_server_handle.get_id()
|
|
403
|
+
proc.pex_server_handle.get_id()
|
|
404
|
+
for proc in self._pex_servers.values()
|
|
405
|
+
if isinstance(proc, PexProcessEntry)
|
|
405
406
|
}
|
|
406
407
|
in_use_handle_ids = self._pending_startup_pex_servers.union(
|
|
407
408
|
self._pending_shutdown_pex_servers
|
|
@@ -19,6 +19,8 @@ DEFAULT_PEX_FILES_DIR = "/tmp/pex-files"
|
|
|
19
19
|
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/customizations/s3.html
|
|
20
20
|
MULTIPART_DOWNLOAD_THREADS = 20 # Double the boto3 default of 10
|
|
21
21
|
|
|
22
|
+
logger = logging.getLogger("dagster.multipex")
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
def _download_from_s3(filename: str, local_filepath: str):
|
|
24
26
|
# Lazy import boto3 to avoid a hard dependency during module load
|
|
@@ -28,7 +30,11 @@ def _download_from_s3(filename: str, local_filepath: str):
|
|
|
28
30
|
|
|
29
31
|
config = Config(retries={"max_attempts": 3, "mode": "standard"})
|
|
30
32
|
|
|
31
|
-
s3 = boto3.client(
|
|
33
|
+
s3 = boto3.client(
|
|
34
|
+
"s3",
|
|
35
|
+
region_name=os.getenv("DAGSTER_CLOUD_SERVERLESS_REGION", "us-west-2"),
|
|
36
|
+
config=config,
|
|
37
|
+
)
|
|
32
38
|
|
|
33
39
|
# TODO: move the bucket and prefix to pex_metdata
|
|
34
40
|
s3_bucket_name = os.environ["DAGSTER_CLOUD_SERVERLESS_STORAGE_S3_BUCKET"]
|
|
@@ -207,7 +213,7 @@ class PexS3Registry:
|
|
|
207
213
|
unused_local_paths = all_local_paths - in_use_local_paths
|
|
208
214
|
unused_paths_present = [path for path in unused_local_paths if os.path.exists(path)]
|
|
209
215
|
if unused_paths_present:
|
|
210
|
-
|
|
216
|
+
logger.info(
|
|
211
217
|
"Cleaning up %s unused local paths: %r",
|
|
212
218
|
len(unused_paths_present),
|
|
213
219
|
unused_paths_present,
|
|
@@ -219,13 +225,13 @@ class PexS3Registry:
|
|
|
219
225
|
else:
|
|
220
226
|
os.remove(path)
|
|
221
227
|
except OSError:
|
|
222
|
-
|
|
228
|
+
logger.exception("Ignoring failure to clean up local unused path %s", path)
|
|
223
229
|
|
|
224
230
|
def venv_for(self, pex_filepath) -> PexVenv:
|
|
225
231
|
_, pex_filename = os.path.split(pex_filepath)
|
|
226
232
|
venv_dir = self.venv_dir_for(pex_filepath)
|
|
227
233
|
if os.path.exists(venv_dir):
|
|
228
|
-
|
|
234
|
+
logger.info("Reusing existing venv %r for %r", venv_dir, pex_filepath)
|
|
229
235
|
else:
|
|
230
236
|
self.install_venv(venv_dir, pex_filepath)
|
|
231
237
|
if not os.path.exists(venv_dir):
|
|
@@ -259,6 +265,7 @@ class PexS3Registry:
|
|
|
259
265
|
# since we combine multiple venvs, we need non hermetic scripts
|
|
260
266
|
"--non-hermetic-scripts",
|
|
261
267
|
venv_dir,
|
|
268
|
+
"--pip",
|
|
262
269
|
],
|
|
263
270
|
stderr=subprocess.STDOUT,
|
|
264
271
|
)
|
|
@@ -267,7 +274,7 @@ class PexS3Registry:
|
|
|
267
274
|
raise PexInstallationError(
|
|
268
275
|
f"Could not install venv. Pex output: {e.output}", pex_filepath
|
|
269
276
|
) from e
|
|
270
|
-
|
|
277
|
+
logger.info(
|
|
271
278
|
"Unpacked pex file %r into venv at %r",
|
|
272
279
|
pex_filepath,
|
|
273
280
|
venv_dir,
|
|
@@ -283,7 +290,7 @@ class PexS3Registry:
|
|
|
283
290
|
if not proc.returncode:
|
|
284
291
|
return Path(proc.stdout.decode("utf-8").strip()).absolute()
|
|
285
292
|
else:
|
|
286
|
-
|
|
293
|
+
logger.error(
|
|
287
294
|
"Cannot determine site-packages for venv at %r: %s\n%s",
|
|
288
295
|
venv_path,
|
|
289
296
|
proc.stdout.decode("utf-8"),
|
|
@@ -315,12 +322,12 @@ class PexS3Registry:
|
|
|
315
322
|
|
|
316
323
|
except subprocess.CalledProcessError:
|
|
317
324
|
# working_directory package is optional, just log a message
|
|
318
|
-
|
|
325
|
+
logger.info("Cannot import working_directory package - not setting current directory.")
|
|
319
326
|
return None
|
|
320
327
|
except OSError:
|
|
321
328
|
# some issue with pex not being runnable, log an error but don't fail yet
|
|
322
329
|
# might fail later if we try to run this again
|
|
323
|
-
|
|
330
|
+
logger.exception(
|
|
324
331
|
"Ignoring failure to run pex file to determine working_directory %r", pex_path
|
|
325
332
|
)
|
|
326
333
|
return None
|
|
@@ -14,7 +14,7 @@ from dagster._grpc.__generated__.dagster_api_pb2_grpc import (
|
|
|
14
14
|
DagsterApiServicer,
|
|
15
15
|
add_DagsterApiServicer_to_server,
|
|
16
16
|
)
|
|
17
|
-
from dagster._grpc.client import DEFAULT_GRPC_TIMEOUT
|
|
17
|
+
from dagster._grpc.client import DEFAULT_GRPC_TIMEOUT, DEFAULT_REPOSITORY_GRPC_TIMEOUT
|
|
18
18
|
from dagster._grpc.server import server_termination_target
|
|
19
19
|
from dagster._grpc.types import GetCurrentRunsResult, SensorExecutionArgs
|
|
20
20
|
from dagster._grpc.utils import max_rx_bytes, max_send_bytes
|
|
@@ -22,12 +22,13 @@ from dagster._serdes import deserialize_value, serialize_value
|
|
|
22
22
|
from dagster._utils.error import SerializableErrorInfo, serializable_error_info_from_exc_info
|
|
23
23
|
from grpc_health.v1 import health, health_pb2, health_pb2_grpc
|
|
24
24
|
|
|
25
|
-
from
|
|
26
|
-
from
|
|
25
|
+
from dagster_cloud.pex.grpc.__generated__ import multi_pex_api_pb2
|
|
26
|
+
from dagster_cloud.pex.grpc.__generated__.multi_pex_api_pb2_grpc import (
|
|
27
27
|
MultiPexApiServicer,
|
|
28
28
|
add_MultiPexApiServicer_to_server,
|
|
29
29
|
)
|
|
30
|
-
from
|
|
30
|
+
from dagster_cloud.pex.grpc.server.manager import MultiPexManager
|
|
31
|
+
from dagster_cloud.pex.grpc.types import (
|
|
31
32
|
CreatePexServerArgs,
|
|
32
33
|
CreatePexServerResponse,
|
|
33
34
|
GetCrashedPexServersArgs,
|
|
@@ -38,7 +39,6 @@ from ..types import (
|
|
|
38
39
|
ShutdownPexServerArgs,
|
|
39
40
|
ShutdownPexServerResponse,
|
|
40
41
|
)
|
|
41
|
-
from .manager import MultiPexManager
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class MultiPexApiServer(MultiPexApiServicer):
|
|
@@ -189,7 +189,12 @@ class DagsterPexProxyApiServer(DagsterApiServicer):
|
|
|
189
189
|
return dagster_api_pb2.ListRepositoriesReply(
|
|
190
190
|
serialized_list_repositories_response_or_error=serialize_value(client_or_error)
|
|
191
191
|
)
|
|
192
|
-
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
return client_or_error._get_response("ListRepositories", request) # noqa: SLF001
|
|
195
|
+
except grpc.RpcError as e:
|
|
196
|
+
# Surface the grpc error to the caller
|
|
197
|
+
context.abort(e.code(), e.details())
|
|
193
198
|
|
|
194
199
|
def Ping(self, request, context):
|
|
195
200
|
return self._query("Ping", request, context)
|
|
@@ -201,7 +206,9 @@ class DagsterPexProxyApiServer(DagsterApiServicer):
|
|
|
201
206
|
return self._query("GetCurrentImage", request, context)
|
|
202
207
|
|
|
203
208
|
def StreamingExternalRepository(self, request, context):
|
|
204
|
-
return self._streaming_query(
|
|
209
|
+
return self._streaming_query(
|
|
210
|
+
"StreamingExternalRepository", request, context, timeout=DEFAULT_REPOSITORY_GRPC_TIMEOUT
|
|
211
|
+
)
|
|
205
212
|
|
|
206
213
|
def Heartbeat(self, request, context):
|
|
207
214
|
return self._query("Heartbeat", request, context)
|
|
@@ -228,7 +235,9 @@ class DagsterPexProxyApiServer(DagsterApiServicer):
|
|
|
228
235
|
return self._query("ExternalPipelineSubsetSnapshot", request, context)
|
|
229
236
|
|
|
230
237
|
def ExternalRepository(self, request, context):
|
|
231
|
-
return self._query(
|
|
238
|
+
return self._query(
|
|
239
|
+
"ExternalRepository", request, context, timeout=DEFAULT_REPOSITORY_GRPC_TIMEOUT
|
|
240
|
+
)
|
|
232
241
|
|
|
233
242
|
def ExternalJob(self, request, context):
|
|
234
243
|
return self._query("ExternalJob", request, context)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
from .loader import DagsterCloudSecretsLoader as DagsterCloudSecretsLoader
|
|
1
|
+
from dagster_cloud.secrets.loader import DagsterCloudSecretsLoader as DagsterCloudSecretsLoader
|
|
@@ -1 +1 @@
|
|
|
1
|
-
from .io_manager import serverless_io_manager as serverless_io_manager
|
|
1
|
+
from dagster_cloud.serverless.io_manager import serverless_io_manager as serverless_io_manager
|
|
@@ -47,7 +47,10 @@ class PickledObjectServerlessIOManager(UPathIOManager):
|
|
|
47
47
|
self._boto_session_expiration.tzinfo
|
|
48
48
|
) + datetime.timedelta(minutes=5):
|
|
49
49
|
self._boto_session, self._boto_session_expiration = self._refresh_boto_session()
|
|
50
|
-
return self._boto_session.client(
|
|
50
|
+
return self._boto_session.client(
|
|
51
|
+
"s3",
|
|
52
|
+
region_name=os.getenv("DAGSTER_CLOUD_SERVERLESS_REGION", "us-west-2"),
|
|
53
|
+
)
|
|
51
54
|
|
|
52
55
|
def load_from_path(self, context: InputContext, path: UPath) -> Any:
|
|
53
56
|
try:
|