Skip to content

experimental/bundletest: offline variable resolution for the local backend - #6632

Draft
Sankalp-Mittal wants to merge 4 commits into
sankalp-mittal/dabs-cloud-backendfrom
sankalp-mittal/dabs-var-resolution
Draft

experimental/bundletest: offline variable resolution for the local backend#6632
Sankalp-Mittal wants to merge 4 commits into
sankalp-mittal/dabs-cloud-backendfrom
sankalp-mittal/dabs-var-resolution

Conversation

@Sankalp-Mittal

Copy link
Copy Markdown
Collaborator

What

The local (DuckDB) backend read databricks.yml with raw yaml.safe_load, so ${var.x} was left literal — local config could diverge from what deploy renders on any interpolated field. This resolves ${var.name} offline and rejects workspace-dependent references loudly.

Stacked on the base (sankalp-mittal/dabs-testing-framework); independent of the resource-handles and cloud PRs (touches only duckdb.py + the example).

How

  • Offline resolution: ${var.name}BUNDLE_VAR_<name> env override, else the declared default (incl. shorthand variables: {name: value}). A variable whose value references another variable resolves too. No auth, no network — the local tier stays offline.
  • Online references skipped loudly: ${workspace.*}, ${resources.*}, a lookup variable, or an unset variable can't be resolved without the workspace. These are not hand-resolved (that would be a reimplementation trap) — they're left literal and raise LocalUnsupported at the use site (get_resource, or a job's sql_task path), so they skip with a reason, never silently pass through as ${...}.

Why this split is safe: plain ${var.name} substitution is a small, stable spec, so resolving it locally won't diverge from DABs; the auth-requiring parts (lookup/workspace) are exactly the ones that aren't locally meaningful anyway (a lookup literally queries the live workspace).

Changes

  • backends/duckdb.py: _resolve_variables in deploy; _online_reference guard in get_resource and on a job's sql_task path in run_job.
  • examples/orders_bundle/databricks.yml: warehouse_id gets a placeholder default so it resolves offline.
  • tests/test_variables.py: default / env-override / nested resolution, and the loud skip for workspace / lookup / unset references.

Verification

cd experimental/bundletest && uv run --extra dev pytest -q   # 24 passed, 1 skipped; ruff clean

This pull request and its description were written by Isaac.

…es loudly

The local backend read databricks.yml with raw yaml.safe_load, so ${var.x},
targets and presets were left literal — local could diverge from deploy on any
interpolated field. Resolve ${var.name} offline (BUNDLE_VAR_* env override, else
the declared default), including a variable whose value references another
variable. No auth, no network — the local tier stays offline.

References only the workspace can resolve — ${workspace.*}, ${resources.*}, a
lookup variable, or an unset variable — are NOT hand-resolved (that would be the
reimplementation trap); they're left literal and rejected loudly via
LocalUnsupported at the use site (get_resource / a job's sql path), never
silently passed through as "${...}". Plain ${var.name} substitution is a small,
stable spec, so resolving it locally is safe; the auth-requiring parts are
exactly the ones that aren't locally meaningful anyway.

The example bundle's warehouse_id now has a placeholder default so it resolves
offline. tests/test_variables.py covers default/env-override/nested resolution
and the loud skip for workspace/lookup/unset references.

Co-authored-by: Isaac <no-reply@databricks.com>
@Sankalp-Mittal
Sankalp-Mittal force-pushed the sankalp-mittal/dabs-var-resolution branch from 04d2b7d to d96098b Compare September 11, 2026 12:59
@Sankalp-Mittal
Sankalp-Mittal changed the base branch from sankalp-mittal/dabs-testing-framework to sankalp-mittal/dabs-cloud-backend September 11, 2026 13:01
@Sankalp-Mittal
Sankalp-Mittal added this pull request to stack #6604 September 11, 2026 13:01
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: c1fb887

Run: 34612726903

Env 🔄​flaky 💚​RECOVERED ✅​pass 🙈​skip Time
💚​ aws linux 1 275 15 6:58
💚​ aws windows 1 277 13 8:49
🔄​ azure linux 2 1 272 15 5:39
💚​ azure windows 1 276 13 7:48
💚​ gcp linux 1 275 15 6:37
💚​ gcp windows 1 277 13 9:06
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🔄​ TestFsCpFileToDirFileNotOverwritten ✅​p ✅​p 🔄​f ✅​p ✅​p ✅​p
🔄​ TestFsCpFileToDirFileNotOverwritten/dbfs_to_uc-volumes ✅​p ✅​p 🔄​f ✅​p ✅​p ✅​p
Top 6 slowest tests (at least 2 minutes):
duration env testname
5:16 aws windows TestAccept
3:26 gcp windows TestAccept
3:24 azure windows TestAccept
2:36 aws windows TestFilerWorkspaceFilesExtensionsReadDir
2:23 aws windows TestFilerWorkspaceFilesExtensionsRead
2:08 aws windows TestFilerRecursiveDelete/workspace_files_extensions

Sankalp-Mittal and others added 3 commits September 11, 2026 14:32
Replace the hand-rolled Python ${var} resolver in the local (DuckDB) backend
with cmd/offline-resolve, a tiny Go helper that loads and resolves the bundle
offline by reusing the CLI's own mutators — no auth, no network, no
reimplementation. deploy() now subprocesses it and parses the resolved config
as JSON, so includes, target overrides, presets, and ${var.*}/${bundle.*} all
resolve exactly as `bundle validate` would (the old resolver read only the
single databricks.yml and handled only ${var.*}).

The helper applies only the mutators that are safe offline, stopping before the
first auth call (PopulateCurrentUser) and skipping ResolveLookupVariables. It
passes offline-only prefixes {bundle, variables} to the resolver so ${workspace.*}
stays literal. A variable the workspace alone can resolve (a lookup or unset
variable) would otherwise abort resolution (SetVariables errors on an unset
required variable; dynvar errors on an unresolvable lookup reference), so the
helper seeds such variables a sentinel default and drops their lookup, letting
resolution complete with the sentinel flowing into the output.

The backend's loud-skip guard now matches both a residual ${...} and the
sentinel, so a resource or sql path that needs a workspace is LocalUnsupported
at the use site with a specific reason. Adds tests proving include and
target-override resolution, which the old resolver could not do.

Co-authored-by: Isaac <no-reply@databricks.com>
The local backend now resolves bundle config by running the in-repo Go helper
cmd/offline-resolve, so deploy()-based tests shell out to `go run`. The
bundletest CI job was uv-only, so add actions/setup-go (pinned to the repo's
go.mod version) before the pytest step, otherwise those tests go red on a
runner without a matching Go. Also note the Go + CLI-repo requirement in the
README.

Co-authored-by: Isaac <no-reply@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants