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.
Files changed (73) hide show
  1. dagster_cloud/__init__.py +3 -3
  2. dagster_cloud/agent/dagster_cloud_agent.py +64 -20
  3. dagster_cloud/agent/instrumentation/run_launch.py +2 -2
  4. dagster_cloud/agent/instrumentation/schedule.py +1 -1
  5. dagster_cloud/agent/instrumentation/sensor.py +1 -1
  6. dagster_cloud/anomaly_detection/__init__.py +2 -2
  7. dagster_cloud/anomaly_detection/defs.py +11 -8
  8. dagster_cloud/api/dagster_cloud_api.py +7 -5
  9. dagster_cloud/auth/constants.py +21 -5
  10. dagster_cloud/batching/__init__.py +1 -1
  11. dagster_cloud/dagster_insights/__init__.py +12 -6
  12. dagster_cloud/dagster_insights/bigquery/dbt_wrapper.py +8 -2
  13. dagster_cloud/dagster_insights/bigquery/insights_bigquery_resource.py +4 -2
  14. dagster_cloud/dagster_insights/insights_utils.py +1 -1
  15. dagster_cloud/dagster_insights/metrics_utils.py +1 -1
  16. dagster_cloud/dagster_insights/snowflake/dagster_snowflake_insights.py +1 -9
  17. dagster_cloud/dagster_insights/snowflake/dbt_wrapper.py +9 -2
  18. dagster_cloud/dagster_insights/snowflake/definitions.py +5 -4
  19. dagster_cloud/dagster_insights/snowflake/insights_snowflake_resource.py +1 -2
  20. dagster_cloud/definitions/__init__.py +0 -0
  21. dagster_cloud/definitions/job_selection.py +36 -0
  22. dagster_cloud/execution/utils/process.py +1 -1
  23. dagster_cloud/instance/__init__.py +81 -42
  24. dagster_cloud/metadata/source_code.py +3 -1
  25. dagster_cloud/opentelemetry/config/exporter.py +1 -1
  26. dagster_cloud/opentelemetry/controller.py +1 -1
  27. dagster_cloud/opentelemetry/observers/dagster_exception_handler.py +1 -1
  28. dagster_cloud/opentelemetry/observers/execution_observer.py +4 -2
  29. dagster_cloud/pex/grpc/__init__.py +2 -2
  30. dagster_cloud/pex/grpc/client.py +2 -2
  31. dagster_cloud/pex/grpc/server/__init__.py +2 -2
  32. dagster_cloud/pex/grpc/server/cli/__init__.py +2 -2
  33. dagster_cloud/pex/grpc/server/manager.py +5 -4
  34. dagster_cloud/pex/grpc/server/registry.py +15 -8
  35. dagster_cloud/pex/grpc/server/server.py +17 -8
  36. dagster_cloud/secrets/__init__.py +1 -1
  37. dagster_cloud/serverless/__init__.py +1 -1
  38. dagster_cloud/serverless/io_manager.py +4 -1
  39. dagster_cloud/storage/compute_logs/__init__.py +3 -1
  40. dagster_cloud/storage/compute_logs/compute_log_manager.py +17 -13
  41. dagster_cloud/storage/defs_state/__init__.py +3 -0
  42. dagster_cloud/storage/defs_state/queries.py +15 -0
  43. dagster_cloud/storage/defs_state/storage.py +113 -0
  44. dagster_cloud/storage/event_logs/__init__.py +3 -1
  45. dagster_cloud/storage/event_logs/storage.py +9 -2
  46. dagster_cloud/storage/event_logs/utils.py +1 -3
  47. dagster_cloud/storage/runs/__init__.py +1 -1
  48. dagster_cloud/storage/runs/queries.py +15 -0
  49. dagster_cloud/storage/runs/storage.py +30 -3
  50. dagster_cloud/storage/schedules/__init__.py +1 -1
  51. dagster_cloud/storage/schedules/storage.py +1 -1
  52. dagster_cloud/util/errors.py +0 -91
  53. dagster_cloud/version.py +1 -1
  54. dagster_cloud/workspace/config_schema/__init__.py +43 -5
  55. dagster_cloud/workspace/docker/__init__.py +8 -7
  56. dagster_cloud/workspace/docker/utils.py +1 -1
  57. dagster_cloud/workspace/ecs/__init__.py +1 -1
  58. dagster_cloud/workspace/ecs/client.py +23 -18
  59. dagster_cloud/workspace/ecs/launcher.py +19 -5
  60. dagster_cloud/workspace/ecs/run_launcher.py +1 -2
  61. dagster_cloud/workspace/ecs/utils.py +5 -2
  62. dagster_cloud/workspace/kubernetes/__init__.py +1 -1
  63. dagster_cloud/workspace/kubernetes/launcher.py +11 -12
  64. dagster_cloud/workspace/kubernetes/utils.py +1 -2
  65. dagster_cloud/workspace/user_code_launcher/__init__.py +5 -3
  66. dagster_cloud/workspace/user_code_launcher/process.py +2 -3
  67. dagster_cloud/workspace/user_code_launcher/user_code_launcher.py +71 -34
  68. dagster_cloud/workspace/user_code_launcher/utils.py +7 -0
  69. {dagster_cloud-1.10.11.dist-info → dagster_cloud-1.12.6.dist-info}/METADATA +9 -8
  70. dagster_cloud-1.12.6.dist-info/RECORD +134 -0
  71. {dagster_cloud-1.10.11.dist-info → dagster_cloud-1.12.6.dist-info}/WHEEL +1 -1
  72. dagster_cloud-1.10.11.dist-info/RECORD +0 -129
  73. {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 = self._get_processed_config(
85
- "dagster_cloud_api", dagster_cloud_api, dagster_cloud_api_config()
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") # pyright: ignore[reportOptionalMemberAccess]
91
- and self._dagster_cloud_api_config.get("deployments") # pyright: ignore[reportOptionalMemberAccess]
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)) # pyright: ignore[reportArgumentType,reportCallIssue]
181
+ new_api_config = dict(copy.deepcopy(self._dagster_cloud_api_config))
162
182
  if deployment_name:
163
- new_api_config["deployment"] = deployment_name # pyright: ignore[reportArgumentType]
183
+ new_api_config["deployment"] = deployment_name
164
184
  if self.includes_branch_deployments:
165
- del new_api_config["branch_deployments"] # pyright: ignore
166
- new_api_config.pop("deployments", None) # pyright: ignore
167
- new_api_config.pop("all_serverless_deployments", None) # pyright: ignore
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) # pyright: ignore
170
- new_api_config.pop("deployments", None) # pyright: ignore
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), # pyright: ignore[reportArgumentType]
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), # pyright: ignore[reportArgumentType]
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), # pyright: ignore[reportArgumentType]
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: # pyright: ignore[reportOptionalMemberAccess]
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"]: # pyright: ignore[reportOptionalSubscript]
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"], # pyright: ignore[reportOptionalSubscript]
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: # pyright: ignore[reportOperatorIssue]
301
- return self._dagster_cloud_api_config["url"] # pyright: ignore[reportOptionalSubscript]
320
+ if "url" in self._dagster_cloud_api_config:
321
+ return self._dagster_cloud_api_config["url"]
302
322
 
303
- organization = get_organization_name_from_agent_token(self.dagster_cloud_agent_token) # pyright: ignore[reportArgumentType]
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 f"https://{organization}.agent.dagster.cloud"
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
- return get_organization_name_from_agent_token(self.dagster_cloud_agent_token) # pyright: ignore[reportArgumentType]
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"): # pyright: ignore[reportOptionalMemberAccess]
330
- return [self._dagster_cloud_api_config["deployment"]] # pyright: ignore[reportOptionalSubscript]
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", []) # pyright: ignore[reportOptionalMemberAccess]
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") # pyright: ignore[reportOptionalMemberAccess,reportReturnType]
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 = get_organization_name_from_agent_token(self.dagster_cloud_agent_token) # pyright: ignore[reportArgumentType]
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") # pyright: ignore[reportOptionalMemberAccess]
348
- return f"https://{organization}.dagster.cloud/" + (f"{deployment}/" if deployment else "")
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
- return self._dagster_cloud_api_config.get("agent_token") # pyright: ignore[reportOptionalMemberAccess]
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"] # pyright: ignore[reportOptionalSubscript]
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"] # pyright: ignore[reportOptionalSubscript]
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") # pyright: ignore[reportOptionalMemberAccess]
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") # pyright: ignore[reportOptionalMemberAccess]
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) # pyright: ignore[reportOptionalMemberAccess]
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.cacheable_assets import CacheableAssetsDefinition
17
+ from dagster._core.definitions.assets.definition.cacheable_assets_definition import (
18
+ CacheableAssetsDefinition,
19
+ )
18
20
 
19
21
  import sys
20
22
 
@@ -1,6 +1,6 @@
1
1
  from dagster import BoolSource, Enum, Field, IntSource, Map, Permissive, Shape, StringSource
2
2
 
3
- from ..enum import (
3
+ from dagster_cloud.opentelemetry.enum import (
4
4
  AggregationTemporalityEnum,
5
5
  CompressionEnum,
6
6
  LoggingExporterEnum,
@@ -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", {}), # pyright: ignore[reportArgumentType]
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 ..controller import OpenTelemetryController
9
- from .dagster_exception_handler import extract_dagster_error_attributes
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,
@@ -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 ..registry import PexS3Registry
13
- from ..server import run_multipex_server
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() for proc in self._pex_servers.values()
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("s3", region_name="us-west-2", config=config)
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
- logging.info(
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
- logging.exception("Ignoring failure to clean up local unused path %s", path)
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
- logging.info("Reusing existing venv %r for %r", venv_dir, pex_filepath)
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
- logging.info(
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
- logging.error(
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
- logging.info("Cannot import working_directory package - not setting current directory.")
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
- logging.exception(
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 ..__generated__ import multi_pex_api_pb2
26
- from ..__generated__.multi_pex_api_pb2_grpc import (
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 ..types import (
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
- return client_or_error._get_response("ListRepositories", request) # noqa: SLF001
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("StreamingExternalRepository", request, context)
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("ExternalRepository", request, context)
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("s3", region_name="us-west-2")
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:
@@ -1 +1,3 @@
1
- from .compute_log_manager import CloudComputeLogManager as CloudComputeLogManager
1
+ from dagster_cloud.storage.compute_logs.compute_log_manager import (
2
+ CloudComputeLogManager as CloudComputeLogManager,
3
+ )