dvt-core 0.58.6__cp311-cp311-macosx_10_9_x86_64.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.
- dbt/__init__.py +7 -0
- dbt/_pydantic_shim.py +26 -0
- dbt/artifacts/__init__.py +0 -0
- dbt/artifacts/exceptions/__init__.py +1 -0
- dbt/artifacts/exceptions/schemas.py +31 -0
- dbt/artifacts/resources/__init__.py +116 -0
- dbt/artifacts/resources/base.py +67 -0
- dbt/artifacts/resources/types.py +93 -0
- dbt/artifacts/resources/v1/analysis.py +10 -0
- dbt/artifacts/resources/v1/catalog.py +23 -0
- dbt/artifacts/resources/v1/components.py +274 -0
- dbt/artifacts/resources/v1/config.py +277 -0
- dbt/artifacts/resources/v1/documentation.py +11 -0
- dbt/artifacts/resources/v1/exposure.py +51 -0
- dbt/artifacts/resources/v1/function.py +52 -0
- dbt/artifacts/resources/v1/generic_test.py +31 -0
- dbt/artifacts/resources/v1/group.py +21 -0
- dbt/artifacts/resources/v1/hook.py +11 -0
- dbt/artifacts/resources/v1/macro.py +29 -0
- dbt/artifacts/resources/v1/metric.py +172 -0
- dbt/artifacts/resources/v1/model.py +145 -0
- dbt/artifacts/resources/v1/owner.py +10 -0
- dbt/artifacts/resources/v1/saved_query.py +111 -0
- dbt/artifacts/resources/v1/seed.py +41 -0
- dbt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dbt/artifacts/resources/v1/semantic_model.py +314 -0
- dbt/artifacts/resources/v1/singular_test.py +14 -0
- dbt/artifacts/resources/v1/snapshot.py +91 -0
- dbt/artifacts/resources/v1/source_definition.py +84 -0
- dbt/artifacts/resources/v1/sql_operation.py +10 -0
- dbt/artifacts/resources/v1/unit_test_definition.py +77 -0
- dbt/artifacts/schemas/__init__.py +0 -0
- dbt/artifacts/schemas/base.py +191 -0
- dbt/artifacts/schemas/batch_results.py +24 -0
- dbt/artifacts/schemas/catalog/__init__.py +11 -0
- dbt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dbt/artifacts/schemas/catalog/v1/catalog.py +59 -0
- dbt/artifacts/schemas/freshness/__init__.py +1 -0
- dbt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dbt/artifacts/schemas/freshness/v3/freshness.py +158 -0
- dbt/artifacts/schemas/manifest/__init__.py +2 -0
- dbt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dbt/artifacts/schemas/manifest/v12/manifest.py +211 -0
- dbt/artifacts/schemas/results.py +147 -0
- dbt/artifacts/schemas/run/__init__.py +2 -0
- dbt/artifacts/schemas/run/v5/__init__.py +0 -0
- dbt/artifacts/schemas/run/v5/run.py +184 -0
- dbt/artifacts/schemas/upgrades/__init__.py +4 -0
- dbt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dbt/artifacts/utils/validation.py +153 -0
- dbt/cli/__init__.py +1 -0
- dbt/cli/context.py +17 -0
- dbt/cli/exceptions.py +57 -0
- dbt/cli/flags.py +560 -0
- dbt/cli/main.py +2403 -0
- dbt/cli/option_types.py +121 -0
- dbt/cli/options.py +80 -0
- dbt/cli/params.py +844 -0
- dbt/cli/requires.py +490 -0
- dbt/cli/resolvers.py +50 -0
- dbt/cli/types.py +40 -0
- dbt/clients/__init__.py +0 -0
- dbt/clients/checked_load.py +83 -0
- dbt/clients/git.py +164 -0
- dbt/clients/jinja.py +206 -0
- dbt/clients/jinja_static.py +245 -0
- dbt/clients/registry.py +192 -0
- dbt/clients/yaml_helper.py +68 -0
- dbt/compilation.py +876 -0
- dbt/compute/__init__.py +14 -0
- dbt/compute/engines/__init__.py +12 -0
- dbt/compute/engines/spark_engine.cpython-311-darwin.so +0 -0
- dbt/compute/engines/spark_engine.py +642 -0
- dbt/compute/federated_executor.cpython-311-darwin.so +0 -0
- dbt/compute/federated_executor.py +1080 -0
- dbt/compute/filter_pushdown.cpython-311-darwin.so +0 -0
- dbt/compute/filter_pushdown.py +273 -0
- dbt/compute/jar_provisioning.cpython-311-darwin.so +0 -0
- dbt/compute/jar_provisioning.py +255 -0
- dbt/compute/java_compat.cpython-311-darwin.so +0 -0
- dbt/compute/java_compat.py +689 -0
- dbt/compute/jdbc_utils.cpython-311-darwin.so +0 -0
- dbt/compute/jdbc_utils.py +678 -0
- dbt/compute/metadata/__init__.py +40 -0
- dbt/compute/metadata/adapters_registry.cpython-311-darwin.so +0 -0
- dbt/compute/metadata/adapters_registry.py +370 -0
- dbt/compute/metadata/registry.cpython-311-darwin.so +0 -0
- dbt/compute/metadata/registry.py +674 -0
- dbt/compute/metadata/store.cpython-311-darwin.so +0 -0
- dbt/compute/metadata/store.py +1499 -0
- dbt/compute/smart_selector.cpython-311-darwin.so +0 -0
- dbt/compute/smart_selector.py +377 -0
- dbt/compute/strategies/__init__.py +55 -0
- dbt/compute/strategies/base.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/base.py +165 -0
- dbt/compute/strategies/dataproc.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/dataproc.py +207 -0
- dbt/compute/strategies/emr.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/emr.py +203 -0
- dbt/compute/strategies/local.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/local.py +443 -0
- dbt/compute/strategies/standalone.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/standalone.py +262 -0
- dbt/config/__init__.py +4 -0
- dbt/config/catalogs.py +94 -0
- dbt/config/compute.cpython-311-darwin.so +0 -0
- dbt/config/compute.py +513 -0
- dbt/config/dvt_profile.cpython-311-darwin.so +0 -0
- dbt/config/dvt_profile.py +342 -0
- dbt/config/profile.py +422 -0
- dbt/config/project.py +873 -0
- dbt/config/project_utils.py +28 -0
- dbt/config/renderer.py +231 -0
- dbt/config/runtime.py +553 -0
- dbt/config/selectors.py +208 -0
- dbt/config/utils.py +77 -0
- dbt/constants.py +28 -0
- dbt/context/__init__.py +0 -0
- dbt/context/base.py +745 -0
- dbt/context/configured.py +135 -0
- dbt/context/context_config.py +382 -0
- dbt/context/docs.py +82 -0
- dbt/context/exceptions_jinja.py +178 -0
- dbt/context/macro_resolver.py +195 -0
- dbt/context/macros.py +171 -0
- dbt/context/manifest.py +72 -0
- dbt/context/providers.py +2249 -0
- dbt/context/query_header.py +13 -0
- dbt/context/secret.py +58 -0
- dbt/context/target.py +74 -0
- dbt/contracts/__init__.py +0 -0
- dbt/contracts/files.py +413 -0
- dbt/contracts/graph/__init__.py +0 -0
- dbt/contracts/graph/manifest.py +1904 -0
- dbt/contracts/graph/metrics.py +97 -0
- dbt/contracts/graph/model_config.py +70 -0
- dbt/contracts/graph/node_args.py +42 -0
- dbt/contracts/graph/nodes.py +1806 -0
- dbt/contracts/graph/semantic_manifest.py +232 -0
- dbt/contracts/graph/unparsed.py +811 -0
- dbt/contracts/project.py +417 -0
- dbt/contracts/results.py +53 -0
- dbt/contracts/selection.py +23 -0
- dbt/contracts/sql.py +85 -0
- dbt/contracts/state.py +68 -0
- dbt/contracts/util.py +46 -0
- dbt/deprecations.py +348 -0
- dbt/deps/__init__.py +0 -0
- dbt/deps/base.py +152 -0
- dbt/deps/git.py +195 -0
- dbt/deps/local.py +79 -0
- dbt/deps/registry.py +130 -0
- dbt/deps/resolver.py +149 -0
- dbt/deps/tarball.py +120 -0
- dbt/docs/source/_ext/dbt_click.py +119 -0
- dbt/docs/source/conf.py +32 -0
- dbt/env_vars.py +64 -0
- dbt/event_time/event_time.py +40 -0
- dbt/event_time/sample_window.py +60 -0
- dbt/events/__init__.py +15 -0
- dbt/events/base_types.py +36 -0
- dbt/events/core_types_pb2.py +2 -0
- dbt/events/logging.py +108 -0
- dbt/events/types.py +2516 -0
- dbt/exceptions.py +1486 -0
- dbt/flags.py +89 -0
- dbt/graph/__init__.py +11 -0
- dbt/graph/cli.py +249 -0
- dbt/graph/graph.py +172 -0
- dbt/graph/queue.py +214 -0
- dbt/graph/selector.py +374 -0
- dbt/graph/selector_methods.py +975 -0
- dbt/graph/selector_spec.py +222 -0
- dbt/graph/thread_pool.py +18 -0
- dbt/hooks.py +21 -0
- dbt/include/README.md +49 -0
- dbt/include/__init__.py +3 -0
- dbt/include/data/adapters_registry.duckdb +0 -0
- dbt/include/data/build_registry.py +242 -0
- dbt/include/data/csv/adapter_queries.csv +33 -0
- dbt/include/data/csv/syntax_rules.csv +9 -0
- dbt/include/data/csv/type_mappings_bigquery.csv +28 -0
- dbt/include/data/csv/type_mappings_databricks.csv +30 -0
- dbt/include/data/csv/type_mappings_mysql.csv +40 -0
- dbt/include/data/csv/type_mappings_oracle.csv +30 -0
- dbt/include/data/csv/type_mappings_postgres.csv +56 -0
- dbt/include/data/csv/type_mappings_redshift.csv +33 -0
- dbt/include/data/csv/type_mappings_snowflake.csv +38 -0
- dbt/include/data/csv/type_mappings_sqlserver.csv +35 -0
- dbt/include/starter_project/.gitignore +4 -0
- dbt/include/starter_project/README.md +15 -0
- dbt/include/starter_project/__init__.py +3 -0
- dbt/include/starter_project/analyses/.gitkeep +0 -0
- dbt/include/starter_project/dbt_project.yml +36 -0
- dbt/include/starter_project/macros/.gitkeep +0 -0
- dbt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dbt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dbt/include/starter_project/models/example/schema.yml +21 -0
- dbt/include/starter_project/seeds/.gitkeep +0 -0
- dbt/include/starter_project/snapshots/.gitkeep +0 -0
- dbt/include/starter_project/tests/.gitkeep +0 -0
- dbt/internal_deprecations.py +26 -0
- dbt/jsonschemas/__init__.py +3 -0
- dbt/jsonschemas/jsonschemas.py +309 -0
- dbt/jsonschemas/project/0.0.110.json +4717 -0
- dbt/jsonschemas/project/0.0.85.json +2015 -0
- dbt/jsonschemas/resources/0.0.110.json +2636 -0
- dbt/jsonschemas/resources/0.0.85.json +2536 -0
- dbt/jsonschemas/resources/latest.json +6773 -0
- dbt/links.py +4 -0
- dbt/materializations/__init__.py +0 -0
- dbt/materializations/incremental/__init__.py +0 -0
- dbt/materializations/incremental/microbatch.py +236 -0
- dbt/mp_context.py +8 -0
- dbt/node_types.py +37 -0
- dbt/parser/__init__.py +23 -0
- dbt/parser/analysis.py +21 -0
- dbt/parser/base.py +548 -0
- dbt/parser/common.py +266 -0
- dbt/parser/docs.py +52 -0
- dbt/parser/fixtures.py +51 -0
- dbt/parser/functions.py +30 -0
- dbt/parser/generic_test.py +100 -0
- dbt/parser/generic_test_builders.py +333 -0
- dbt/parser/hooks.py +118 -0
- dbt/parser/macros.py +137 -0
- dbt/parser/manifest.py +2204 -0
- dbt/parser/models.py +573 -0
- dbt/parser/partial.py +1178 -0
- dbt/parser/read_files.py +445 -0
- dbt/parser/schema_generic_tests.py +422 -0
- dbt/parser/schema_renderer.py +111 -0
- dbt/parser/schema_yaml_readers.py +935 -0
- dbt/parser/schemas.py +1466 -0
- dbt/parser/search.py +149 -0
- dbt/parser/seeds.py +28 -0
- dbt/parser/singular_test.py +20 -0
- dbt/parser/snapshots.py +44 -0
- dbt/parser/sources.py +558 -0
- dbt/parser/sql.py +62 -0
- dbt/parser/unit_tests.py +621 -0
- dbt/plugins/__init__.py +20 -0
- dbt/plugins/contracts.py +9 -0
- dbt/plugins/exceptions.py +2 -0
- dbt/plugins/manager.py +163 -0
- dbt/plugins/manifest.py +21 -0
- dbt/profiler.py +20 -0
- dbt/py.typed +1 -0
- dbt/query_analyzer.cpython-311-darwin.so +0 -0
- dbt/query_analyzer.py +410 -0
- dbt/runners/__init__.py +2 -0
- dbt/runners/exposure_runner.py +7 -0
- dbt/runners/no_op_runner.py +45 -0
- dbt/runners/saved_query_runner.py +7 -0
- dbt/selected_resources.py +8 -0
- dbt/task/__init__.py +0 -0
- dbt/task/base.py +503 -0
- dbt/task/build.py +197 -0
- dbt/task/clean.py +56 -0
- dbt/task/clone.py +161 -0
- dbt/task/compile.py +150 -0
- dbt/task/compute.cpython-311-darwin.so +0 -0
- dbt/task/compute.py +458 -0
- dbt/task/debug.py +505 -0
- dbt/task/deps.py +280 -0
- dbt/task/docs/__init__.py +3 -0
- dbt/task/docs/api/__init__.py +23 -0
- dbt/task/docs/api/catalog.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/catalog.py +204 -0
- dbt/task/docs/api/lineage.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/lineage.py +234 -0
- dbt/task/docs/api/profile.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/profile.py +204 -0
- dbt/task/docs/api/spark.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/spark.py +186 -0
- dbt/task/docs/generate.py +947 -0
- dbt/task/docs/index.html +250 -0
- dbt/task/docs/serve.cpython-311-darwin.so +0 -0
- dbt/task/docs/serve.py +174 -0
- dbt/task/dvt_output.py +362 -0
- dbt/task/dvt_run.py +204 -0
- dbt/task/freshness.py +322 -0
- dbt/task/function.py +121 -0
- dbt/task/group_lookup.py +46 -0
- dbt/task/init.cpython-311-darwin.so +0 -0
- dbt/task/init.py +604 -0
- dbt/task/java.cpython-311-darwin.so +0 -0
- dbt/task/java.py +316 -0
- dbt/task/list.py +236 -0
- dbt/task/metadata.cpython-311-darwin.so +0 -0
- dbt/task/metadata.py +804 -0
- dbt/task/printer.py +175 -0
- dbt/task/profile.cpython-311-darwin.so +0 -0
- dbt/task/profile.py +1307 -0
- dbt/task/profile_serve.py +615 -0
- dbt/task/retract.py +438 -0
- dbt/task/retry.py +175 -0
- dbt/task/run.py +1387 -0
- dbt/task/run_operation.py +141 -0
- dbt/task/runnable.py +758 -0
- dbt/task/seed.py +103 -0
- dbt/task/show.py +149 -0
- dbt/task/snapshot.py +56 -0
- dbt/task/spark.cpython-311-darwin.so +0 -0
- dbt/task/spark.py +414 -0
- dbt/task/sql.py +110 -0
- dbt/task/target_sync.cpython-311-darwin.so +0 -0
- dbt/task/target_sync.py +766 -0
- dbt/task/test.py +464 -0
- dbt/tests/fixtures/__init__.py +1 -0
- dbt/tests/fixtures/project.py +620 -0
- dbt/tests/util.py +651 -0
- dbt/tracking.py +529 -0
- dbt/utils/__init__.py +3 -0
- dbt/utils/artifact_upload.py +151 -0
- dbt/utils/utils.py +408 -0
- dbt/version.py +270 -0
- dvt_cli/__init__.py +72 -0
- dvt_core-0.58.6.dist-info/METADATA +288 -0
- dvt_core-0.58.6.dist-info/RECORD +324 -0
- dvt_core-0.58.6.dist-info/WHEEL +5 -0
- dvt_core-0.58.6.dist-info/entry_points.txt +2 -0
- dvt_core-0.58.6.dist-info/top_level.txt +2 -0
dbt/task/compute.py
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Compute Task
|
|
3
|
+
|
|
4
|
+
Handles DVT compute engine management commands:
|
|
5
|
+
- test: List all compute engines with connection status
|
|
6
|
+
- edit: Open computes.yml in user's editor
|
|
7
|
+
- validate: Validate compute engine configurations
|
|
8
|
+
|
|
9
|
+
v0.5.97: Simplified CLI - removed register/remove (use dvt compute edit instead)
|
|
10
|
+
computes.yml with comprehensive samples replaces interactive registration.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
from dbt.config.compute import ComputeRegistry, SparkPlatform, DEFAULT_COMPUTES_YAML
|
|
20
|
+
from dbt_common.exceptions import DbtRuntimeError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ComputeTask:
|
|
24
|
+
"""Task for managing DVT compute engines."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, project_dir=None):
|
|
27
|
+
"""
|
|
28
|
+
Initialize ComputeTask.
|
|
29
|
+
|
|
30
|
+
:param project_dir: Path to project root directory (str or Path, defaults to cwd)
|
|
31
|
+
"""
|
|
32
|
+
# Convert Path to string for consistent handling
|
|
33
|
+
if project_dir is not None:
|
|
34
|
+
self.project_dir = str(project_dir)
|
|
35
|
+
else:
|
|
36
|
+
self.project_dir = str(Path.cwd())
|
|
37
|
+
self.registry = ComputeRegistry(self.project_dir)
|
|
38
|
+
|
|
39
|
+
def list_computes(self) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
List all compute engines with their names and basic info.
|
|
42
|
+
|
|
43
|
+
v0.51.1: Simple list command for quick reference.
|
|
44
|
+
|
|
45
|
+
:returns: True always (for CLI exit code)
|
|
46
|
+
"""
|
|
47
|
+
clusters = self.registry.list()
|
|
48
|
+
|
|
49
|
+
if not clusters:
|
|
50
|
+
print("No compute engines configured.")
|
|
51
|
+
print("\nRun 'dvt compute edit' to configure compute engines.")
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
print(f"\nCompute Engines ({len(clusters)} configured)")
|
|
55
|
+
print("-" * 40)
|
|
56
|
+
|
|
57
|
+
for cluster in clusters:
|
|
58
|
+
default_marker = " (default)" if cluster.name == self.registry.target_compute else ""
|
|
59
|
+
platform = cluster.detect_platform()
|
|
60
|
+
print(f" {cluster.name}{default_marker}")
|
|
61
|
+
print(f" Platform: {platform.value}")
|
|
62
|
+
if cluster.description:
|
|
63
|
+
print(f" Description: {cluster.description}")
|
|
64
|
+
|
|
65
|
+
print("")
|
|
66
|
+
print(f"Default: {self.registry.target_compute}")
|
|
67
|
+
print(f"Config: {self.registry.get_config_path()}")
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def test_single_compute(self, compute_name: str) -> bool:
|
|
72
|
+
"""
|
|
73
|
+
Test a specific compute engine by name.
|
|
74
|
+
|
|
75
|
+
v0.5.99: Added single compute testing via `dvt compute test <name>`.
|
|
76
|
+
|
|
77
|
+
:param compute_name: Name of the compute engine to test
|
|
78
|
+
:returns: True if test passes, False otherwise
|
|
79
|
+
"""
|
|
80
|
+
clusters = self.registry.list()
|
|
81
|
+
cluster = None
|
|
82
|
+
|
|
83
|
+
# Find the cluster by name
|
|
84
|
+
for c in clusters:
|
|
85
|
+
if c.name == compute_name:
|
|
86
|
+
cluster = c
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
if cluster is None:
|
|
90
|
+
print(f"❌ Compute engine '{compute_name}' not found.")
|
|
91
|
+
print(f"\nAvailable compute engines:")
|
|
92
|
+
for c in clusters:
|
|
93
|
+
default_marker = " (default)" if c.name == self.registry.target_compute else ""
|
|
94
|
+
print(f" - {c.name}{default_marker}")
|
|
95
|
+
print(f"\nRun 'dvt compute edit' to configure compute engines.")
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
# Test the specific cluster
|
|
99
|
+
print(f"\n" + "=" * 70)
|
|
100
|
+
print(f"Testing Compute Engine: {compute_name}")
|
|
101
|
+
print("=" * 70)
|
|
102
|
+
|
|
103
|
+
default_marker = " (default)" if cluster.name == self.registry.target_compute else ""
|
|
104
|
+
platform = cluster.detect_platform()
|
|
105
|
+
|
|
106
|
+
print(f"\n{cluster.name}{default_marker}")
|
|
107
|
+
print(f" Type: {cluster.type}")
|
|
108
|
+
print(f" Platform: {platform.value}")
|
|
109
|
+
if cluster.description:
|
|
110
|
+
print(f" Description: {cluster.description}")
|
|
111
|
+
|
|
112
|
+
# Test the cluster
|
|
113
|
+
status, message = self._test_single_cluster(cluster)
|
|
114
|
+
|
|
115
|
+
if status == "ok":
|
|
116
|
+
print(f" Status: ✅ {message}")
|
|
117
|
+
elif status == "warning":
|
|
118
|
+
print(f" Status: ⚠️ {message}")
|
|
119
|
+
elif status == "error":
|
|
120
|
+
print(f" Status: ❌ {message}")
|
|
121
|
+
|
|
122
|
+
print("\n" + "-" * 70)
|
|
123
|
+
|
|
124
|
+
return status in ("ok", "warning")
|
|
125
|
+
|
|
126
|
+
def _test_single_cluster(self, cluster) -> tuple:
|
|
127
|
+
"""
|
|
128
|
+
Test a single cluster and return status.
|
|
129
|
+
|
|
130
|
+
v0.5.98: Full connectivity testing with three stages:
|
|
131
|
+
1. Config validation
|
|
132
|
+
2. Session creation + SELECT 1 test
|
|
133
|
+
3. (Optional) JDBC read test
|
|
134
|
+
|
|
135
|
+
v0.51.1: Enhanced session isolation - forcefully stop ALL Spark sessions
|
|
136
|
+
before and after each test to prevent config bleed between computes.
|
|
137
|
+
|
|
138
|
+
:param cluster: ComputeCluster to test
|
|
139
|
+
:returns: Tuple of (status, message) where status is 'ok', 'warning', or 'error'
|
|
140
|
+
"""
|
|
141
|
+
platform = cluster.detect_platform()
|
|
142
|
+
|
|
143
|
+
if cluster.type == "spark":
|
|
144
|
+
# Stage 1: Config validation
|
|
145
|
+
config_result = self._validate_cluster_config(cluster, platform)
|
|
146
|
+
if config_result[0] == "error":
|
|
147
|
+
return config_result
|
|
148
|
+
|
|
149
|
+
# Stage 2: Full connectivity test (session + SQL)
|
|
150
|
+
try:
|
|
151
|
+
# v0.51.1: Force stop ALL Spark sessions before testing
|
|
152
|
+
# This ensures each compute engine test gets a completely fresh JVM context
|
|
153
|
+
self._force_stop_all_spark_sessions()
|
|
154
|
+
|
|
155
|
+
strategy = self._get_strategy_for_cluster(cluster, platform)
|
|
156
|
+
if strategy is None:
|
|
157
|
+
return config_result # Return config validation result
|
|
158
|
+
|
|
159
|
+
success, message = strategy.test_connectivity()
|
|
160
|
+
|
|
161
|
+
# v0.51.1: Force stop ALL sessions after test to not interfere with next compute
|
|
162
|
+
self._force_stop_all_spark_sessions()
|
|
163
|
+
|
|
164
|
+
if success:
|
|
165
|
+
return ("ok", message)
|
|
166
|
+
else:
|
|
167
|
+
return ("error", message)
|
|
168
|
+
|
|
169
|
+
except ImportError as e:
|
|
170
|
+
# Missing dependency (PySpark, databricks-connect, etc.)
|
|
171
|
+
return ("warning", str(e))
|
|
172
|
+
except AttributeError as e:
|
|
173
|
+
# databricks-connect may have compatibility issues with pyspark
|
|
174
|
+
if "Hook" in str(e) or "SparkSession" in str(e):
|
|
175
|
+
return ("warning", f"databricks-connect/pyspark version conflict: {str(e)[:50]}")
|
|
176
|
+
return ("error", f"Connectivity test failed: {str(e)}")
|
|
177
|
+
except Exception as e:
|
|
178
|
+
return ("error", f"Connectivity test failed: {str(e)}")
|
|
179
|
+
|
|
180
|
+
return ("ok", "Configuration valid")
|
|
181
|
+
|
|
182
|
+
def _force_stop_all_spark_sessions(self) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Force stop ALL Spark sessions to ensure complete isolation.
|
|
185
|
+
|
|
186
|
+
v0.51.1: This is critical for compute testing because:
|
|
187
|
+
1. Different computes have different spark.jars.packages configs
|
|
188
|
+
2. Spark's getOrCreate() returns existing session without re-applying config
|
|
189
|
+
3. We need a fresh JVM context for each compute's JDBC drivers
|
|
190
|
+
|
|
191
|
+
This method:
|
|
192
|
+
1. Stops active session
|
|
193
|
+
2. Clears the local session cache used by LocalStrategy
|
|
194
|
+
3. Forces garbage collection to release JVM resources
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
from pyspark.sql import SparkSession
|
|
198
|
+
|
|
199
|
+
# Stop active session
|
|
200
|
+
active = SparkSession.getActiveSession()
|
|
201
|
+
if active:
|
|
202
|
+
active.stop()
|
|
203
|
+
|
|
204
|
+
# Clear local strategy cache
|
|
205
|
+
try:
|
|
206
|
+
from dbt.compute.strategies.local import _SPARK_SESSION_CACHE
|
|
207
|
+
_SPARK_SESSION_CACHE.clear()
|
|
208
|
+
except (ImportError, AttributeError):
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
# Give JVM time to release resources
|
|
212
|
+
import time
|
|
213
|
+
time.sleep(0.5)
|
|
214
|
+
|
|
215
|
+
except ImportError:
|
|
216
|
+
pass # PySpark not installed
|
|
217
|
+
except Exception:
|
|
218
|
+
pass # Best effort cleanup
|
|
219
|
+
|
|
220
|
+
def _validate_cluster_config(self, cluster, platform: SparkPlatform) -> tuple:
|
|
221
|
+
"""
|
|
222
|
+
Validate cluster configuration (Stage 1).
|
|
223
|
+
|
|
224
|
+
:param cluster: ComputeCluster to validate
|
|
225
|
+
:param platform: Detected SparkPlatform
|
|
226
|
+
:returns: Tuple of (status, message)
|
|
227
|
+
"""
|
|
228
|
+
if platform == SparkPlatform.LOCAL:
|
|
229
|
+
try:
|
|
230
|
+
import pyspark # noqa: F401
|
|
231
|
+
# PySpark 4.0+ doesn't have __version__ attribute, use importlib
|
|
232
|
+
try:
|
|
233
|
+
from importlib.metadata import version
|
|
234
|
+
pyspark_version = version("pyspark")
|
|
235
|
+
except Exception:
|
|
236
|
+
pyspark_version = "unknown"
|
|
237
|
+
return ("ok", f"PySpark {pyspark_version} available")
|
|
238
|
+
except ImportError:
|
|
239
|
+
return ("error", "PySpark not installed")
|
|
240
|
+
|
|
241
|
+
elif platform == SparkPlatform.EMR:
|
|
242
|
+
required = ["master"]
|
|
243
|
+
missing = [k for k in required if k not in cluster.config]
|
|
244
|
+
if missing:
|
|
245
|
+
return ("error", f"Missing config: {', '.join(missing)}")
|
|
246
|
+
master = cluster.config.get("master", "")
|
|
247
|
+
if master.lower() != "yarn":
|
|
248
|
+
return ("error", f"EMR requires master='yarn', got: {master}")
|
|
249
|
+
return ("ok", "EMR config valid")
|
|
250
|
+
|
|
251
|
+
elif platform == SparkPlatform.DATAPROC:
|
|
252
|
+
required = ["project", "region", "cluster"]
|
|
253
|
+
missing = [k for k in required if k not in cluster.config]
|
|
254
|
+
if missing:
|
|
255
|
+
return ("error", f"Missing config: {', '.join(missing)}")
|
|
256
|
+
return ("ok", "Dataproc config valid")
|
|
257
|
+
|
|
258
|
+
elif platform == SparkPlatform.STANDALONE:
|
|
259
|
+
master = cluster.config.get("master", "")
|
|
260
|
+
if not master.startswith("spark://"):
|
|
261
|
+
return ("error", f"Standalone requires master='spark://...', got: {master}")
|
|
262
|
+
return ("ok", f"Standalone config valid ({master})")
|
|
263
|
+
|
|
264
|
+
else:
|
|
265
|
+
# External/generic
|
|
266
|
+
if "master" in cluster.config:
|
|
267
|
+
return ("ok", f"External cluster at {cluster.config['master']}")
|
|
268
|
+
return ("ok", "Configuration valid")
|
|
269
|
+
|
|
270
|
+
def _get_strategy_for_cluster(self, cluster, platform: SparkPlatform):
|
|
271
|
+
"""
|
|
272
|
+
Get the connection strategy for a cluster.
|
|
273
|
+
|
|
274
|
+
:param cluster: ComputeCluster
|
|
275
|
+
:param platform: Detected SparkPlatform
|
|
276
|
+
:returns: BaseConnectionStrategy instance or None
|
|
277
|
+
"""
|
|
278
|
+
try:
|
|
279
|
+
if platform == SparkPlatform.LOCAL:
|
|
280
|
+
from dbt.compute.strategies.local import LocalStrategy
|
|
281
|
+
return LocalStrategy(cluster.config, app_name=f"DVT-{cluster.name}")
|
|
282
|
+
|
|
283
|
+
elif platform == SparkPlatform.EMR:
|
|
284
|
+
from dbt.compute.strategies import get_emr_strategy
|
|
285
|
+
EMRStrategy = get_emr_strategy()
|
|
286
|
+
return EMRStrategy(cluster.config, app_name=f"DVT-{cluster.name}")
|
|
287
|
+
|
|
288
|
+
elif platform == SparkPlatform.DATAPROC:
|
|
289
|
+
from dbt.compute.strategies import get_dataproc_strategy
|
|
290
|
+
DataprocStrategy = get_dataproc_strategy()
|
|
291
|
+
return DataprocStrategy(cluster.config, app_name=f"DVT-{cluster.name}")
|
|
292
|
+
|
|
293
|
+
elif platform == SparkPlatform.STANDALONE:
|
|
294
|
+
from dbt.compute.strategies import get_standalone_strategy
|
|
295
|
+
StandaloneStrategy = get_standalone_strategy()
|
|
296
|
+
return StandaloneStrategy(cluster.config, app_name=f"DVT-{cluster.name}")
|
|
297
|
+
|
|
298
|
+
else:
|
|
299
|
+
# External - no specific strategy, skip connectivity test
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
except ImportError as e:
|
|
303
|
+
raise ImportError(f"Missing dependency for {platform.value}: {str(e)}")
|
|
304
|
+
|
|
305
|
+
def edit_config(self) -> bool:
|
|
306
|
+
"""
|
|
307
|
+
Open computes.yml in user's preferred editor.
|
|
308
|
+
|
|
309
|
+
Uses EDITOR environment variable, falls back to common editors.
|
|
310
|
+
|
|
311
|
+
:returns: True if editor launched successfully
|
|
312
|
+
"""
|
|
313
|
+
# Ensure config exists with full template
|
|
314
|
+
config_path = self.registry.ensure_config_exists()
|
|
315
|
+
|
|
316
|
+
# If file doesn't have full samples, write the template
|
|
317
|
+
with open(config_path, "r") as f:
|
|
318
|
+
content = f.read()
|
|
319
|
+
if "DATABRICKS" not in content:
|
|
320
|
+
# Write full template to get all the samples
|
|
321
|
+
with open(config_path, "w") as f:
|
|
322
|
+
f.write(DEFAULT_COMPUTES_YAML)
|
|
323
|
+
|
|
324
|
+
print(f"Opening: {config_path}")
|
|
325
|
+
print("")
|
|
326
|
+
print("After editing, run 'dvt compute validate' to check syntax.")
|
|
327
|
+
print("")
|
|
328
|
+
|
|
329
|
+
# Get editor from environment or use defaults
|
|
330
|
+
editor = os.environ.get("EDITOR")
|
|
331
|
+
if not editor:
|
|
332
|
+
editor = os.environ.get("VISUAL")
|
|
333
|
+
if not editor:
|
|
334
|
+
# Try common editors
|
|
335
|
+
for ed in ["code", "nano", "vim", "vi", "notepad"]:
|
|
336
|
+
try:
|
|
337
|
+
subprocess.run(["which", ed], capture_output=True, check=True)
|
|
338
|
+
editor = ed
|
|
339
|
+
break
|
|
340
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
341
|
+
continue
|
|
342
|
+
|
|
343
|
+
if not editor:
|
|
344
|
+
print(f"No editor found. Please open manually: {config_path}")
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
# Handle VS Code specially (--wait flag)
|
|
349
|
+
if editor in ("code", "code-insiders"):
|
|
350
|
+
subprocess.run([editor, "--wait", str(config_path)])
|
|
351
|
+
else:
|
|
352
|
+
subprocess.run([editor, str(config_path)])
|
|
353
|
+
|
|
354
|
+
# Reload and validate after edit
|
|
355
|
+
print("\nValidating changes...")
|
|
356
|
+
return self.validate_config()
|
|
357
|
+
|
|
358
|
+
except Exception as e:
|
|
359
|
+
print(f"Error opening editor: {e}", file=sys.stderr)
|
|
360
|
+
print(f"Please open manually: {config_path}")
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
def validate_config(self) -> bool:
|
|
364
|
+
"""
|
|
365
|
+
Validate computes.yml syntax and configuration.
|
|
366
|
+
|
|
367
|
+
:returns: True if configuration is valid
|
|
368
|
+
"""
|
|
369
|
+
config_path = self.registry.get_config_path()
|
|
370
|
+
|
|
371
|
+
if not config_path.exists():
|
|
372
|
+
print(f"✗ Config file not found: {config_path}")
|
|
373
|
+
print("\nRun 'dvt compute edit' to create one.")
|
|
374
|
+
return False
|
|
375
|
+
|
|
376
|
+
print(f"Validating: {config_path}")
|
|
377
|
+
print("")
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
# Try to load the YAML
|
|
381
|
+
import yaml
|
|
382
|
+
with open(config_path, "r") as f:
|
|
383
|
+
data = yaml.safe_load(f)
|
|
384
|
+
|
|
385
|
+
if not data:
|
|
386
|
+
print("✗ Config file is empty")
|
|
387
|
+
return False
|
|
388
|
+
|
|
389
|
+
errors = []
|
|
390
|
+
warnings = []
|
|
391
|
+
|
|
392
|
+
# Check target_compute
|
|
393
|
+
target = data.get("target_compute")
|
|
394
|
+
if not target:
|
|
395
|
+
errors.append("Missing 'target_compute' field")
|
|
396
|
+
|
|
397
|
+
# Check computes section
|
|
398
|
+
computes = data.get("computes", {})
|
|
399
|
+
if not computes:
|
|
400
|
+
errors.append("No compute engines defined in 'computes' section")
|
|
401
|
+
else:
|
|
402
|
+
# Validate each compute
|
|
403
|
+
for name, config in computes.items():
|
|
404
|
+
if config is None:
|
|
405
|
+
continue # Skip commented-out entries
|
|
406
|
+
|
|
407
|
+
if not isinstance(config, dict):
|
|
408
|
+
errors.append(f"Compute '{name}': invalid configuration (expected dict)")
|
|
409
|
+
continue
|
|
410
|
+
|
|
411
|
+
# Check type
|
|
412
|
+
compute_type = config.get("type")
|
|
413
|
+
if not compute_type:
|
|
414
|
+
errors.append(f"Compute '{name}': missing 'type' field")
|
|
415
|
+
elif compute_type not in ("spark",):
|
|
416
|
+
warnings.append(f"Compute '{name}': unknown type '{compute_type}' (only 'spark' supported)")
|
|
417
|
+
|
|
418
|
+
# Check config section
|
|
419
|
+
if "config" not in config:
|
|
420
|
+
warnings.append(f"Compute '{name}': no 'config' section (will use defaults)")
|
|
421
|
+
|
|
422
|
+
# Check target_compute references valid engine
|
|
423
|
+
if target and target not in computes:
|
|
424
|
+
errors.append(f"target_compute '{target}' not found in computes section")
|
|
425
|
+
|
|
426
|
+
# Print results
|
|
427
|
+
if errors:
|
|
428
|
+
print("Errors:")
|
|
429
|
+
for err in errors:
|
|
430
|
+
print(f" ✗ {err}")
|
|
431
|
+
print("")
|
|
432
|
+
|
|
433
|
+
if warnings:
|
|
434
|
+
print("Warnings:")
|
|
435
|
+
for warn in warnings:
|
|
436
|
+
print(f" ⚠ {warn}")
|
|
437
|
+
print("")
|
|
438
|
+
|
|
439
|
+
if not errors and not warnings:
|
|
440
|
+
print("✓ Configuration is valid")
|
|
441
|
+
print(f" Target compute: {target}")
|
|
442
|
+
print(f" Engines defined: {len([c for c in computes.values() if c])}")
|
|
443
|
+
return True
|
|
444
|
+
|
|
445
|
+
if not errors:
|
|
446
|
+
print("✓ Configuration is valid (with warnings)")
|
|
447
|
+
return True
|
|
448
|
+
|
|
449
|
+
return False
|
|
450
|
+
|
|
451
|
+
except yaml.YAMLError as e:
|
|
452
|
+
print(f"✗ YAML syntax error:")
|
|
453
|
+
print(f" {e}")
|
|
454
|
+
return False
|
|
455
|
+
|
|
456
|
+
except Exception as e:
|
|
457
|
+
print(f"✗ Validation failed: {e}")
|
|
458
|
+
return False
|