Some checks failed
CI / skinny-install (aco) (push) Successful in 49s
CI / skinny-install (api) (push) Successful in 30s
CI / lint-test (push) Successful in 1m27s
CI / skinny-install (bcda) (push) Successful in 33s
CI / skinny-install (bib) (push) Successful in 30s
CI / skinny-install (ccw) (push) Successful in 27s
CI / skinny-install (bls) (push) Successful in 30s
CI / skinny-install (cms) (push) Successful in 26s
CI / skinny-install (cli) (push) Successful in 34s
CI / skinny-install (conf) (push) Successful in 25s
CI / skinny-install (perf) (push) Successful in 29s
CI / skinny-install (pfs) (push) Successful in 32s
CI / skinny-install (rex) (push) Successful in 26s
Infra CI / notebooks (push) Successful in 6s
Infra CI / zotero (push) Successful in 7s
Infra CI / docs (push) Successful in 8s
Infra CI / api (push) Successful in 9s
Infra CI / mc (push) Successful in 7s
Package Supply Chain / pkg-supply-chain (push) Failing after 52s
Deploy / build-scan-report (push) Successful in 3m21s
Replace 375-line OrderedDict YAML generator with typed Python resources via databricks-bundles 0.295.0. databricks.yml shrinks from 278 lines to 23 — delegates all resource definitions to bundle/resources/__init__.py via python.resources entry point. - bundle/resources/__init__.py: load_resources() returns Resources with Job, Schema, Volume from pipeline registry - databricks.yml: minimal (bundle name, targets, python entry point) - Replaced databricks-cli>=0.18.0 with databricks-bundles>=0.295.0 - 30 DAB tests passing (12 new Python resource tests) - 12,067 total tests passing Refs #224
141 lines
4.0 KiB
Python
141 lines
4.0 KiB
Python
"""Databricks Asset Bundle resources — Python-defined.
|
|
|
|
Called by the DAB CLI at deploy time via::
|
|
|
|
# databricks.yml
|
|
python:
|
|
resources:
|
|
- "resources:load_resources"
|
|
|
|
Generates Job, Schema, and Volume resources from the pipeline
|
|
registry, eliminating the need for YAML generation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from databricks.bundles.core import Resources, Variable
|
|
from databricks.bundles.jobs import (
|
|
CronSchedule,
|
|
Job,
|
|
JobCluster,
|
|
Library,
|
|
PythonWheelTask,
|
|
Task,
|
|
TaskDependency,
|
|
)
|
|
from databricks.bundles.schemas import Schema
|
|
from databricks.bundles.volumes import Volume, VolumeType
|
|
|
|
|
|
def _pipeline_registry() -> dict:
|
|
"""Import the pipeline registry. Returns empty dict if unavailable."""
|
|
try:
|
|
from aco.pipe import registry
|
|
|
|
return registry
|
|
except ImportError:
|
|
return {}
|
|
|
|
|
|
def _pipeline_schemas(registry: dict) -> list[str]:
|
|
"""Derive schema names from pipeline expression outputs."""
|
|
schemas: set[str] = set()
|
|
for pipe in registry.values():
|
|
for expr in pipe.exprs:
|
|
name = expr.name if isinstance(expr.name, str) else expr[0]
|
|
if "." in name:
|
|
schemas.add(name.split(".")[0])
|
|
return sorted(schemas)
|
|
|
|
|
|
def _config() -> dict:
|
|
"""Read [databricks] section from stack.toml."""
|
|
try:
|
|
from conf import cfg
|
|
|
|
return cfg.databricks.to_dict()
|
|
except (ImportError, AttributeError):
|
|
return {}
|
|
|
|
|
|
def load_resources() -> Resources:
|
|
"""Generate all DAB resources from the pipeline registry."""
|
|
registry = _pipeline_registry()
|
|
cfg = _config()
|
|
schema_names = _pipeline_schemas(registry)
|
|
|
|
catalog = Variable(path="catalog", type=str)
|
|
|
|
# Schemas — one per pipeline-produced schema
|
|
schemas = {
|
|
name: Schema(
|
|
name=name,
|
|
catalog_name=catalog,
|
|
)
|
|
for name in schema_names
|
|
}
|
|
|
|
# Volume — staging area for data uploads
|
|
volume = Volume(
|
|
name="staging",
|
|
catalog_name=catalog,
|
|
schema_name="default",
|
|
volume_type=VolumeType.MANAGED,
|
|
)
|
|
|
|
# Job cluster
|
|
cluster_cfg = cfg.get("clusters", {}).get("default", {})
|
|
job_cluster = JobCluster(
|
|
job_cluster_key="default_cluster",
|
|
new_cluster={
|
|
"spark_version": cluster_cfg.get("spark_version", "15.4.x-scala2.12"),
|
|
"node_type_id": cluster_cfg.get("node_type_id", "i3.xlarge"),
|
|
"num_workers": cluster_cfg.get("num_workers", 1),
|
|
},
|
|
)
|
|
|
|
# Tasks — one per pipeline
|
|
package_name = cfg.get("package_name", "stack")
|
|
entry_point = cfg.get("entry_point", "cli")
|
|
tasks = []
|
|
for pipe_name, pipe in sorted(registry.items()):
|
|
task = Task(
|
|
task_key=pipe_name,
|
|
depends_on=[TaskDependency(task_key=dep) for dep in pipe.upstream] or None,
|
|
job_cluster_key="default_cluster",
|
|
python_wheel_task=PythonWheelTask(
|
|
package_name=package_name,
|
|
entry_point=entry_point,
|
|
named_parameters={
|
|
"run": pipe_name,
|
|
"--target": "spark",
|
|
"--catalog": catalog,
|
|
"--save": "",
|
|
},
|
|
),
|
|
libraries=[Library(whl="dbfs:/FileStore/wheels/stack-latest.whl")],
|
|
)
|
|
tasks.append(task)
|
|
|
|
# Schedule
|
|
schedule_expr = cfg.get("schedule", "0 0 6 * * ?")
|
|
timezone = cfg.get("timezone", "America/New_York")
|
|
|
|
job = Job(
|
|
name=f"{cfg.get('bundle_name', 'stack')}-pipelines",
|
|
description=f"Run all {len(registry)} ACO pipelines in dependency order",
|
|
schedule=CronSchedule(
|
|
quartz_cron_expression=schedule_expr,
|
|
timezone_id=timezone,
|
|
),
|
|
job_clusters=[job_cluster],
|
|
tasks=tasks,
|
|
)
|
|
|
|
resources = Resources()
|
|
resources.add_job("stack_pipelines", job)
|
|
for name, schema in schemas.items():
|
|
resources.add_schema(name, schema)
|
|
resources.add_volume("staging", volume)
|
|
return resources
|