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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import socket
|
|
3
3
|
import uuid
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
4
5
|
from contextlib import ExitStack
|
|
5
6
|
from functools import lru_cache
|
|
6
|
-
from typing import TYPE_CHECKING, Any,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
7
8
|
|
|
8
9
|
import yaml
|
|
9
10
|
from dagster import (
|
|
@@ -19,6 +20,7 @@ from dagster._core.instance.config import config_field_for_configurable_class
|
|
|
19
20
|
from dagster._core.instance.ref import InstanceRef, configurable_class_data
|
|
20
21
|
from dagster._core.launcher import DefaultRunLauncher, RunLauncher
|
|
21
22
|
from dagster._core.storage.dagster_run import DagsterRun
|
|
23
|
+
from dagster._core.storage.defs_state.base import DefsStateStorage
|
|
22
24
|
from dagster._serdes import ConfigurableClassData
|
|
23
25
|
from dagster_cloud_cli.core.graphql_client import (
|
|
24
26
|
create_agent_graphql_client,
|
|
@@ -30,10 +32,12 @@ from dagster_cloud_cli.core.headers.auth import DagsterCloudInstanceScope
|
|
|
30
32
|
from urllib3 import Retry
|
|
31
33
|
|
|
32
34
|
from dagster_cloud.agent import AgentQueuesConfig
|
|
33
|
-
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from
|
|
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
|
|
40
|
+
from dagster_cloud.version import __version__
|
|
37
41
|
|
|
38
42
|
if TYPE_CHECKING:
|
|
39
43
|
from requests import Session
|
|
@@ -48,6 +52,23 @@ class DagsterCloudInstance(DagsterInstance):
|
|
|
48
52
|
def telemetry_enabled(self) -> bool:
|
|
49
53
|
return False
|
|
50
54
|
|
|
55
|
+
@property
|
|
56
|
+
def run_retries_max_retries(self) -> int:
|
|
57
|
+
raise NotImplementedError(
|
|
58
|
+
"run_retries.max_retries is a deployment setting and can only be accessed by a DeploymentScopedHostInstance"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def run_retries_retry_on_asset_or_op_failure(self) -> bool:
|
|
63
|
+
raise NotImplementedError(
|
|
64
|
+
"run_retries.retry_on_asset_or_op_failure is a deployment setting and can only be accessed by a DeploymentScopedHostInstance"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def defs_state_storage(self) -> Optional[DefsStateStorage]:
|
|
69
|
+
# only DeploymentScopedHostInstance / DagsterCloudAgentInstance have a defs state storage
|
|
70
|
+
return None
|
|
71
|
+
|
|
51
72
|
|
|
52
73
|
class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
53
74
|
def __init__(
|
|
@@ -59,13 +80,18 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
59
80
|
isolated_agents=None,
|
|
60
81
|
agent_queues=None,
|
|
61
82
|
agent_metrics=None,
|
|
83
|
+
opentelemetry=None,
|
|
62
84
|
**kwargs,
|
|
63
85
|
):
|
|
64
86
|
super().__init__(*args, **kwargs)
|
|
65
87
|
|
|
66
88
|
self._unprocessed_dagster_cloud_api_config = dagster_cloud_api
|
|
67
|
-
self._dagster_cloud_api_config =
|
|
68
|
-
|
|
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
|
+
)
|
|
69
95
|
)
|
|
70
96
|
|
|
71
97
|
check.invariant(
|
|
@@ -117,12 +143,29 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
117
143
|
processed_agent_queues_config = self._get_processed_config(
|
|
118
144
|
"agent_queues", agent_queues, self._agent_queues_config_schema()
|
|
119
145
|
)
|
|
120
|
-
self.agent_queues_config = AgentQueuesConfig(**processed_agent_queues_config)
|
|
146
|
+
self.agent_queues_config = AgentQueuesConfig(**processed_agent_queues_config) # pyright: ignore[reportCallIssue]
|
|
147
|
+
|
|
148
|
+
self._opentelemetry_config: Optional[Mapping[str, Any]] = self._get_processed_config(
|
|
149
|
+
"opentelemetry", opentelemetry, opentelemetry_config_schema()
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
self._opentelemetry_controller: Optional[OpenTelemetryController] = None
|
|
121
153
|
|
|
122
154
|
self._instance_uuid = str(uuid.uuid4())
|
|
123
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
|
+
|
|
124
167
|
def _get_processed_config(
|
|
125
|
-
self, name: str, config: Optional[
|
|
168
|
+
self, name: str, config: Optional[dict[str, Any]], config_type: dict[str, Any]
|
|
126
169
|
):
|
|
127
170
|
config_dict = check.opt_dict_param(config, "config", key_type=str)
|
|
128
171
|
processed_config = process_config(config_type, config_dict)
|
|
@@ -140,15 +183,11 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
140
183
|
new_api_config["deployment"] = deployment_name
|
|
141
184
|
if self.includes_branch_deployments:
|
|
142
185
|
del new_api_config["branch_deployments"]
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if "all_serverless_deployments" in new_api_config:
|
|
146
|
-
del new_api_config["all_serverless_deployments"]
|
|
186
|
+
new_api_config.pop("deployments", None)
|
|
187
|
+
new_api_config.pop("all_serverless_deployments", None)
|
|
147
188
|
else:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if "deployments" in new_api_config:
|
|
151
|
-
del new_api_config["deployments"]
|
|
189
|
+
new_api_config.pop("deployment", None)
|
|
190
|
+
new_api_config.pop("deployments", None)
|
|
152
191
|
|
|
153
192
|
return new_api_config
|
|
154
193
|
|
|
@@ -187,7 +226,7 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
187
226
|
return create_agent_graphql_client(
|
|
188
227
|
self.client_managed_retries_requests_session,
|
|
189
228
|
self.dagster_cloud_graphql_url,
|
|
190
|
-
self._dagster_cloud_api_config,
|
|
229
|
+
self._dagster_cloud_api_config, # pyright: ignore[reportArgumentType]
|
|
191
230
|
scope=scope,
|
|
192
231
|
)
|
|
193
232
|
|
|
@@ -270,7 +309,7 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
270
309
|
if self._http_client is None:
|
|
271
310
|
self._http_client = create_agent_http_client(
|
|
272
311
|
self.client_managed_retries_requests_session,
|
|
273
|
-
self._dagster_cloud_api_config,
|
|
312
|
+
self._dagster_cloud_api_config, # pyright: ignore[reportArgumentType]
|
|
274
313
|
scope=DagsterCloudInstanceScope.DEPLOYMENT,
|
|
275
314
|
)
|
|
276
315
|
|
|
@@ -281,18 +320,23 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
281
320
|
if "url" in self._dagster_cloud_api_config:
|
|
282
321
|
return self._dagster_cloud_api_config["url"]
|
|
283
322
|
|
|
284
|
-
organization =
|
|
323
|
+
organization, region = decode_agent_token(self.dagster_cloud_agent_token)
|
|
285
324
|
if not organization:
|
|
286
325
|
raise DagsterInvariantViolationError(
|
|
287
326
|
"Could not derive Dagster Cloud URL from agent token. Create a new agent token or"
|
|
288
327
|
" set the `url` field under `dagster_cloud_api` in your `dagster.yaml`."
|
|
289
328
|
)
|
|
290
329
|
|
|
291
|
-
return
|
|
330
|
+
return (
|
|
331
|
+
f"https://{organization}.agent.{region}.dagster.cloud"
|
|
332
|
+
if region
|
|
333
|
+
else f"https://{organization}.agent.dagster.cloud"
|
|
334
|
+
)
|
|
292
335
|
|
|
293
336
|
@property
|
|
294
337
|
def organization_name(self) -> Optional[str]:
|
|
295
|
-
|
|
338
|
+
organization, _ = decode_agent_token(self.dagster_cloud_agent_token)
|
|
339
|
+
return organization
|
|
296
340
|
|
|
297
341
|
@property
|
|
298
342
|
def deployment_name(self) -> Optional[str]:
|
|
@@ -306,7 +350,7 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
306
350
|
return deployment_names[0]
|
|
307
351
|
|
|
308
352
|
@property
|
|
309
|
-
def deployment_names(self) ->
|
|
353
|
+
def deployment_names(self) -> list[str]:
|
|
310
354
|
if self._dagster_cloud_api_config.get("deployment"):
|
|
311
355
|
return [self._dagster_cloud_api_config["deployment"]]
|
|
312
356
|
|
|
@@ -314,11 +358,11 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
314
358
|
|
|
315
359
|
@property
|
|
316
360
|
def include_all_serverless_deployments(self) -> bool:
|
|
317
|
-
return self._dagster_cloud_api_config.get("all_serverless_deployments")
|
|
361
|
+
return self._dagster_cloud_api_config.get("all_serverless_deployments") or False
|
|
318
362
|
|
|
319
363
|
@property
|
|
320
364
|
def dagit_url(self):
|
|
321
|
-
organization =
|
|
365
|
+
organization, region = decode_agent_token(self.dagster_cloud_agent_token)
|
|
322
366
|
if not organization:
|
|
323
367
|
raise Exception(
|
|
324
368
|
"Could not derive Dagster Cloud URL from agent token to generate a Dagit URL."
|
|
@@ -326,7 +370,12 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
326
370
|
)
|
|
327
371
|
|
|
328
372
|
deployment = self._dagster_cloud_api_config.get("deployment")
|
|
329
|
-
|
|
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 "")
|
|
330
379
|
|
|
331
380
|
@property
|
|
332
381
|
def dagster_cloud_graphql_url(self):
|
|
@@ -360,12 +409,29 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
360
409
|
def dagster_cloud_upload_api_response_url(self):
|
|
361
410
|
return f"{self.dagster_cloud_url}/upload_api_response"
|
|
362
411
|
|
|
412
|
+
@property
|
|
413
|
+
def dagster_cloud_check_snapshot_url(self):
|
|
414
|
+
return f"{self.dagster_cloud_url}/check_snapshot"
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
def dagster_cloud_confirm_upload_url(self):
|
|
418
|
+
return f"{self.dagster_cloud_url}/confirm_upload"
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def dagster_cloud_code_location_update_result_url(self):
|
|
422
|
+
return f"{self.dagster_cloud_url}/code_location_update_result"
|
|
423
|
+
|
|
363
424
|
def dagster_cloud_api_headers(self, scope: DagsterCloudInstanceScope):
|
|
364
|
-
return get_agent_headers(self._dagster_cloud_api_config, scope=scope)
|
|
425
|
+
return get_agent_headers(self._dagster_cloud_api_config, scope=scope) # pyright: ignore[reportArgumentType]
|
|
365
426
|
|
|
366
427
|
@property
|
|
367
|
-
def dagster_cloud_agent_token(self):
|
|
368
|
-
|
|
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"]
|
|
369
435
|
|
|
370
436
|
@property
|
|
371
437
|
def dagster_cloud_api_retries(self) -> int:
|
|
@@ -376,10 +442,10 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
376
442
|
return self._dagster_cloud_api_config["timeout"]
|
|
377
443
|
|
|
378
444
|
@property
|
|
379
|
-
def dagster_cloud_api_proxies(self) -> Optional[
|
|
445
|
+
def dagster_cloud_api_proxies(self) -> Optional[dict[str, str]]:
|
|
380
446
|
# Requests library modifies the proxies key so create a copy
|
|
381
447
|
return (
|
|
382
|
-
self._dagster_cloud_api_config.get("proxies").copy()
|
|
448
|
+
self._dagster_cloud_api_config.get("proxies").copy() # pyright: ignore[reportOptionalMemberAccess]
|
|
383
449
|
if self._dagster_cloud_api_config.get("proxies")
|
|
384
450
|
else {}
|
|
385
451
|
)
|
|
@@ -404,7 +470,7 @@ class DagsterCloudAgentInstance(DagsterCloudInstance):
|
|
|
404
470
|
return f"Agent {self.instance_uuid[:8]}"
|
|
405
471
|
|
|
406
472
|
@property
|
|
407
|
-
def dagster_cloud_api_env_vars(self) ->
|
|
473
|
+
def dagster_cloud_api_env_vars(self) -> list[str]:
|
|
408
474
|
return get_env_names_from_config(
|
|
409
475
|
dagster_cloud_api_config(), self._unprocessed_dagster_cloud_api_config
|
|
410
476
|
)
|
|
@@ -470,6 +536,9 @@ instance_class:
|
|
|
470
536
|
cls._isolated_agents_config_schema(), is_required=False
|
|
471
537
|
), # deprecated in favor of isolated_agents
|
|
472
538
|
"agent_queues": Field(cls._agent_queues_config_schema(), is_required=False),
|
|
539
|
+
"opentelemetry": Field(
|
|
540
|
+
opentelemetry_config_schema(), is_required=False, default_value={"enabled": False}
|
|
541
|
+
),
|
|
473
542
|
}
|
|
474
543
|
|
|
475
544
|
@classmethod
|
|
@@ -496,22 +565,22 @@ instance_class:
|
|
|
496
565
|
|
|
497
566
|
empty_yaml = yaml.dump({})
|
|
498
567
|
|
|
499
|
-
defaults["run_storage"] = ConfigurableClassData(
|
|
568
|
+
defaults["run_storage"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
500
569
|
"dagster_cloud.storage.runs",
|
|
501
570
|
"GraphQLRunStorage",
|
|
502
571
|
empty_yaml,
|
|
503
572
|
)
|
|
504
|
-
defaults["event_log_storage"] = ConfigurableClassData(
|
|
573
|
+
defaults["event_log_storage"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
505
574
|
"dagster_cloud.storage.event_logs",
|
|
506
575
|
"GraphQLEventLogStorage",
|
|
507
576
|
empty_yaml,
|
|
508
577
|
)
|
|
509
|
-
defaults["schedule_storage"] = ConfigurableClassData(
|
|
578
|
+
defaults["schedule_storage"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
510
579
|
"dagster_cloud.storage.schedules",
|
|
511
580
|
"GraphQLScheduleStorage",
|
|
512
581
|
empty_yaml,
|
|
513
582
|
)
|
|
514
|
-
defaults["storage"] = ConfigurableClassData(
|
|
583
|
+
defaults["storage"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
515
584
|
module_name="dagster.core.storage.legacy_storage",
|
|
516
585
|
class_name="CompositeStorage",
|
|
517
586
|
config_yaml=yaml.dump(
|
|
@@ -536,18 +605,25 @@ instance_class:
|
|
|
536
605
|
),
|
|
537
606
|
)
|
|
538
607
|
|
|
539
|
-
defaults["compute_logs"] = ConfigurableClassData(
|
|
608
|
+
defaults["compute_logs"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
540
609
|
"dagster_cloud.storage.compute_logs", "CloudComputeLogManager", empty_yaml
|
|
541
610
|
)
|
|
542
611
|
|
|
543
|
-
defaults["secrets"] = ConfigurableClassData(
|
|
612
|
+
defaults["secrets"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
544
613
|
"dagster_cloud.secrets", "DagsterCloudSecretsLoader", empty_yaml
|
|
545
614
|
)
|
|
546
615
|
|
|
616
|
+
defaults["defs_state_storage"] = ConfigurableClassData( # pyright: ignore[reportIndexIssue]
|
|
617
|
+
"dagster_cloud.storage.defs_state", "GraphQLDefsStateStorage", empty_yaml
|
|
618
|
+
)
|
|
619
|
+
|
|
547
620
|
return defaults
|
|
548
621
|
|
|
549
622
|
def dispose(self) -> None:
|
|
550
623
|
super().dispose()
|
|
624
|
+
if self._opentelemetry_controller:
|
|
625
|
+
self._opentelemetry_controller.dispose()
|
|
626
|
+
self._opentelemetry_controller = None
|
|
551
627
|
self._exit_stack.close()
|
|
552
628
|
|
|
553
629
|
@property
|
|
@@ -568,6 +644,17 @@ instance_class:
|
|
|
568
644
|
# potentially overridden interval in the serverless user code launcher
|
|
569
645
|
return 30
|
|
570
646
|
|
|
647
|
+
@property
|
|
648
|
+
def opentelemetry(self) -> OpenTelemetryController:
|
|
649
|
+
if not self._opentelemetry_controller:
|
|
650
|
+
self._opentelemetry_controller = OpenTelemetryController(
|
|
651
|
+
instance_id=self.instance_uuid,
|
|
652
|
+
version=__version__,
|
|
653
|
+
config=self._opentelemetry_config,
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
return self._opentelemetry_controller
|
|
657
|
+
|
|
571
658
|
|
|
572
659
|
@lru_cache(maxsize=100) # Scales on order of active branch deployments
|
|
573
660
|
def _cached_inject_deployment(
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from contextlib import AbstractContextManager, contextmanager
|
|
3
|
+
from typing import Optional, Protocol
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Instrumentation(Protocol):
|
|
7
|
+
def tags(self, tags: list[str]) -> "Instrumentation": ...
|
|
8
|
+
|
|
9
|
+
def histogram(self, name: str, value: float) -> None: ...
|
|
10
|
+
|
|
11
|
+
def increment(self, name: str) -> None: ...
|
|
12
|
+
|
|
13
|
+
def instrument_context(
|
|
14
|
+
self, name: str, buckets_ms: Optional[list[int]]
|
|
15
|
+
) -> AbstractContextManager[None]: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NoOpInstrumentation(Instrumentation):
|
|
19
|
+
def tags(self, tags: list[str]) -> Instrumentation:
|
|
20
|
+
return self
|
|
21
|
+
|
|
22
|
+
def histogram(self, name: str, value: float) -> None:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def increment(self, name: str) -> None:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@contextmanager
|
|
29
|
+
def instrument_context(
|
|
30
|
+
self, name: str, buckets_ms: Optional[list[int]]
|
|
31
|
+
) -> Generator[None, None, None]:
|
|
32
|
+
yield
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from collections.abc import Sequence
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
from typing import TYPE_CHECKING, Optional,
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
4
5
|
|
|
5
6
|
from dagster import DagsterInvariantViolationError
|
|
6
|
-
from dagster._annotations import
|
|
7
|
+
from dagster._annotations import beta
|
|
7
8
|
from dagster._core.definitions.metadata import (
|
|
8
9
|
AnchorBasedFilePathMapping,
|
|
9
10
|
link_code_references_to_git,
|
|
@@ -12,8 +13,10 @@ from dagster._core.definitions.metadata.source_code import FilePathMapping
|
|
|
12
13
|
from dagster._core.types.loadable_target_origin import LoadableTargetOrigin
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
|
-
from dagster import AssetsDefinition, SourceAsset
|
|
16
|
-
from dagster._core.definitions.
|
|
16
|
+
from dagster import AssetsDefinition, AssetSpec, SourceAsset
|
|
17
|
+
from dagster._core.definitions.assets.definition.cacheable_assets_definition import (
|
|
18
|
+
CacheableAssetsDefinition,
|
|
19
|
+
)
|
|
17
20
|
|
|
18
21
|
import sys
|
|
19
22
|
|
|
@@ -33,7 +36,7 @@ def _locate_git_root() -> Optional[Path]:
|
|
|
33
36
|
elif code_origin.python_file:
|
|
34
37
|
code_origin_filepath = code_origin.python_file
|
|
35
38
|
|
|
36
|
-
if not code_origin_filepath:
|
|
39
|
+
if not code_origin_filepath: # pyright: ignore[reportPossiblyUnboundVariable]
|
|
37
40
|
return None
|
|
38
41
|
current_dir = Path(code_origin_filepath)
|
|
39
42
|
for parent in current_dir.parents:
|
|
@@ -42,13 +45,15 @@ def _locate_git_root() -> Optional[Path]:
|
|
|
42
45
|
return None
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
@
|
|
48
|
+
@beta
|
|
46
49
|
def link_code_references_to_git_if_cloud(
|
|
47
|
-
assets_defs: Sequence[
|
|
50
|
+
assets_defs: Sequence[
|
|
51
|
+
Union["AssetsDefinition", "SourceAsset", "CacheableAssetsDefinition", "AssetSpec"]
|
|
52
|
+
],
|
|
48
53
|
git_url: Optional[str] = None,
|
|
49
54
|
git_branch: Optional[str] = None,
|
|
50
55
|
file_path_mapping: Optional[FilePathMapping] = None,
|
|
51
|
-
) -> Sequence[Union["AssetsDefinition", "SourceAsset", "CacheableAssetsDefinition"]]:
|
|
56
|
+
) -> Sequence[Union["AssetsDefinition", "SourceAsset", "CacheableAssetsDefinition", "AssetSpec"]]:
|
|
52
57
|
"""Wrapper function which converts local file path code references to hosted git URLs
|
|
53
58
|
if running in a Dagster Plus cloud environment. This is determined by the presence of
|
|
54
59
|
the `DAGSTER_CLOUD_DEPLOYMENT_NAME` environment variable. When running in any other context,
|
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
|
|
7
|
+
# A minimal copy of the standard OTEL API
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Span:
|
|
11
|
+
def __init__(self, name: str):
|
|
12
|
+
self._name = name
|
|
13
|
+
self._attrs = {}
|
|
14
|
+
self._start_time = time.time()
|
|
15
|
+
self._end_time = None
|
|
16
|
+
|
|
17
|
+
def set_attribute(self, key: str, value: Union[int, str]):
|
|
18
|
+
self._attrs[key] = value
|
|
19
|
+
|
|
20
|
+
def end(self):
|
|
21
|
+
self._end_time = time.time()
|
|
22
|
+
|
|
23
|
+
def get_serialized(self) -> str:
|
|
24
|
+
# base64 to make it safe for a http header (no newlines, few special chars)
|
|
25
|
+
return base64.b64encode(
|
|
26
|
+
json.dumps(
|
|
27
|
+
{
|
|
28
|
+
"_name": self._name,
|
|
29
|
+
"_duration": self._end_time - self._start_time
|
|
30
|
+
if self._end_time is not None
|
|
31
|
+
else None,
|
|
32
|
+
**self._attrs,
|
|
33
|
+
},
|
|
34
|
+
separators=(",", ":"),
|
|
35
|
+
indent=None,
|
|
36
|
+
).encode("utf-8")
|
|
37
|
+
).decode()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Tracer:
|
|
41
|
+
def __init__(self):
|
|
42
|
+
self._spans: list[Span] = []
|
|
43
|
+
|
|
44
|
+
@contextmanager
|
|
45
|
+
def start_span(self, name: str):
|
|
46
|
+
span = Span(name)
|
|
47
|
+
try:
|
|
48
|
+
yield span
|
|
49
|
+
finally:
|
|
50
|
+
span.end()
|
|
51
|
+
self._spans.append(span)
|
|
52
|
+
|
|
53
|
+
def pop_serialized_span(self) -> Optional[str]:
|
|
54
|
+
try:
|
|
55
|
+
span = self._spans.pop(0)
|
|
56
|
+
except IndexError:
|
|
57
|
+
return None
|
|
58
|
+
else:
|
|
59
|
+
return span.get_serialized()
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from dagster import BoolSource, Field, Shape, StringSource
|
|
2
|
+
|
|
3
|
+
from dagster_cloud.opentelemetry.config.exporter import exporter_config_schema
|
|
4
|
+
from dagster_cloud.opentelemetry.config.log_record_processor import log_record_processor_schema
|
|
5
|
+
from dagster_cloud.opentelemetry.config.logging_handler import logging_handler_schema
|
|
6
|
+
from dagster_cloud.opentelemetry.config.meter_provider import meter_provider_config_schema
|
|
7
|
+
from dagster_cloud.opentelemetry.config.metric_reader import metric_reader_config_schema
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def opentelemetry_config_schema():
|
|
11
|
+
return {
|
|
12
|
+
"enabled": Field(
|
|
13
|
+
BoolSource,
|
|
14
|
+
description="Enables OpenTelemetry instrumentation.",
|
|
15
|
+
is_required=False,
|
|
16
|
+
default_value=True,
|
|
17
|
+
),
|
|
18
|
+
"service_name": Field(
|
|
19
|
+
StringSource,
|
|
20
|
+
description="Name of the service to which to send telemetry",
|
|
21
|
+
is_required=False,
|
|
22
|
+
default_value="dagster-cloud-agent",
|
|
23
|
+
),
|
|
24
|
+
"logging": Field(
|
|
25
|
+
description="The logging configuration.",
|
|
26
|
+
is_required=False,
|
|
27
|
+
config=Shape(
|
|
28
|
+
fields={
|
|
29
|
+
"enabled": Field(
|
|
30
|
+
BoolSource,
|
|
31
|
+
description="Enables logging instrumentation.",
|
|
32
|
+
is_required=False,
|
|
33
|
+
default_value=False,
|
|
34
|
+
),
|
|
35
|
+
"exporter": exporter_config_schema("logging"),
|
|
36
|
+
"processor": log_record_processor_schema(),
|
|
37
|
+
"handler": logging_handler_schema(),
|
|
38
|
+
},
|
|
39
|
+
),
|
|
40
|
+
),
|
|
41
|
+
"metrics": Field(
|
|
42
|
+
description="The metrics configuration.",
|
|
43
|
+
is_required=False,
|
|
44
|
+
config=Shape(
|
|
45
|
+
fields={
|
|
46
|
+
"enabled": Field(
|
|
47
|
+
BoolSource,
|
|
48
|
+
description="Enables metrics instrumentation.",
|
|
49
|
+
is_required=False,
|
|
50
|
+
default_value=False,
|
|
51
|
+
),
|
|
52
|
+
"exporter": exporter_config_schema("metrics"),
|
|
53
|
+
"reader": metric_reader_config_schema(),
|
|
54
|
+
"provider": meter_provider_config_schema(),
|
|
55
|
+
},
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
# TODO: Add tracing configuration when it's supported.
|
|
59
|
+
# "tracing": Field(
|
|
60
|
+
# description="The tracing configuration.",
|
|
61
|
+
# config=Shape(
|
|
62
|
+
# fields={
|
|
63
|
+
# "enabled": Field(
|
|
64
|
+
# BoolSource,
|
|
65
|
+
# description="Enables tracing instrumentation.",
|
|
66
|
+
# is_required=False,
|
|
67
|
+
# default_value=False,
|
|
68
|
+
# ),
|
|
69
|
+
# "exporter": exporter_config_schema("tracing"),
|
|
70
|
+
# },
|
|
71
|
+
# ),
|
|
72
|
+
# ),
|
|
73
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from dagster import BoolSource, Enum, Field, IntSource, Map, Permissive, Shape, StringSource
|
|
2
|
+
|
|
3
|
+
from dagster_cloud.opentelemetry.enum import (
|
|
4
|
+
AggregationTemporalityEnum,
|
|
5
|
+
CompressionEnum,
|
|
6
|
+
LoggingExporterEnum,
|
|
7
|
+
MetricsExporterEnum,
|
|
8
|
+
MetricsInstrumentTypesEnum,
|
|
9
|
+
TracingExporterEnum,
|
|
10
|
+
ViewAggregationEnum,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Convert Python to Dagster enums
|
|
14
|
+
compression_enum = Enum.from_python_enum_direct_values(CompressionEnum)
|
|
15
|
+
logging_exporter_enum = Enum.from_python_enum_direct_values(LoggingExporterEnum)
|
|
16
|
+
metrics_exporter_enum = Enum.from_python_enum_direct_values(MetricsExporterEnum)
|
|
17
|
+
tracing_exporter_enum = Enum.from_python_enum_direct_values(TracingExporterEnum)
|
|
18
|
+
|
|
19
|
+
aggregation_temporality_enum = Enum.from_python_enum_direct_values(AggregationTemporalityEnum)
|
|
20
|
+
metrics_instrument_types_enum = Enum.from_python_enum_direct_values(MetricsInstrumentTypesEnum)
|
|
21
|
+
view_aggregation_enum = Enum.from_python_enum_direct_values(ViewAggregationEnum)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def exporter_config_schema(pillar: str) -> Shape:
|
|
25
|
+
"""Different observability pillars have different supported exporters but share common parameters.
|
|
26
|
+
Valid pillars are: logging, metrics, tracing.
|
|
27
|
+
"""
|
|
28
|
+
exporter_type_field: Field
|
|
29
|
+
exporter_extra_params: dict = {}
|
|
30
|
+
if pillar == "logging":
|
|
31
|
+
exporter_type_field = Field(
|
|
32
|
+
config=logging_exporter_enum,
|
|
33
|
+
is_required=False,
|
|
34
|
+
default_value=LoggingExporterEnum.ConsoleLogExporter.value,
|
|
35
|
+
)
|
|
36
|
+
elif pillar == "metrics":
|
|
37
|
+
exporter_type_field = Field(
|
|
38
|
+
config=metrics_exporter_enum,
|
|
39
|
+
is_required=False,
|
|
40
|
+
default_value=MetricsExporterEnum.ConsoleMetricExporter.value,
|
|
41
|
+
)
|
|
42
|
+
exporter_extra_params = {
|
|
43
|
+
"preferred_temporality": Field(
|
|
44
|
+
config=Map(str, aggregation_temporality_enum), is_required=False
|
|
45
|
+
),
|
|
46
|
+
"preferred_aggregation": Field(
|
|
47
|
+
config=Map(str, view_aggregation_enum), is_required=False
|
|
48
|
+
),
|
|
49
|
+
}
|
|
50
|
+
elif pillar == "tracing":
|
|
51
|
+
exporter_type_field = Field(
|
|
52
|
+
config=tracing_exporter_enum,
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError(f"Invalid pillar: {pillar}")
|
|
56
|
+
|
|
57
|
+
return Shape(
|
|
58
|
+
description="Provide configuration for an OpenTelemetry exporter.",
|
|
59
|
+
fields={
|
|
60
|
+
"type": exporter_type_field,
|
|
61
|
+
"params": Permissive(
|
|
62
|
+
fields={
|
|
63
|
+
# These fields are common to most exporters. No defaults are provided, to allow either
|
|
64
|
+
# OpenTelemetry's default or environment variables to be used.
|
|
65
|
+
# Ref: https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html
|
|
66
|
+
"endpoint": Field(StringSource, is_required=False),
|
|
67
|
+
"compression": Field(config=compression_enum, is_required=False),
|
|
68
|
+
"headers": Field(
|
|
69
|
+
description="Custom headers to send", config=Permissive(), is_required=False
|
|
70
|
+
),
|
|
71
|
+
"certificate_file": Field(StringSource, is_required=False),
|
|
72
|
+
"client_key_file": Field(StringSource, is_required=False),
|
|
73
|
+
"client_certificate_file": Field(StringSource, is_required=False),
|
|
74
|
+
"insecure": Field(BoolSource, is_required=False),
|
|
75
|
+
"timeout": Field(IntSource, is_required=False),
|
|
76
|
+
# These fields are specific an exporter.
|
|
77
|
+
**exporter_extra_params,
|
|
78
|
+
}
|
|
79
|
+
),
|
|
80
|
+
},
|
|
81
|
+
)
|