Skip to content

Add libraries field to the clusters resource - #6831

Merged
Sankalp-Mittal merged 12 commits into
mainfrom
sankalp-mittal/cluster-libraries
Sep 28, 2026
Merged

Sankalp-Mittal merged 12 commits into
mainfrom
sankalp-mittal/cluster-libraries

Conversation

@Sankalp-Mittal

@Sankalp-Mittal Sankalp-Mittal commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Changes

Re-lands #6365 (reverted in #6830) with the regression fix folded in. Adds resources.clusters.<name>.libraries in the direct engine: libraries are installed on all-purpose clusters through the Libraries API (install, uninstall, cluster-status) as part of the cluster lifecycle.

Compared to #6365:

  • Libraries are managed only when a cluster declares a libraries section. Without one, the cluster behaves as before the field existed: libraries installed by job runs or out of band are not drift, there is no Updated clusters.*, and no install or uninstall calls are made. The plan still reads cluster-status and records the libraries as skipped with the new reason unmanaged (done in the cluster's OverrideChangeDesc).
  • No cluster restarts for library changes. Installs apply live on a running cluster. Uninstalls are marked UNINSTALL_ON_RESTART by the backend and take effect at the cluster's next restart.
  • The testserver now behaves like cloud for job task libraries. A job run installs its task libraries on its existing_cluster_id cluster, so this class of regression now fails locally instead of only in the CloudSlow nightly.

Library behaviour by config:

Cluster config Library calls Result
No libraries section read only unchanged; libraries from job runs are skipped as unmanaged
libraries section removed read only cluster unchanged; previously declared libraries stay installed, unmanaged
libraries: [] read + uninstall all libraries on the cluster are uninstalled
libraries declared read + install/uninstall declared libraries are managed; other libraries on the cluster are uninstalled

Why

#6365 regressed the nightly integration_whl/interactive_* tests. A job task on an existing cluster installs its libraries cluster-wide, and they persist after the run. The direct engine read them as drift even on clusters that declared no libraries, restarted the cluster on every deploy, and uninstalled them.

Decisions, from the team discussion and review with @andrewnester:

  • A cluster without a libraries section must behave as before: no change detection, no uninstalls, no restarts. Change detection and uninstall calls happen only when the section is used.
  • Never restart an all-purpose cluster for a library change: that kills attached sessions and running work. An opt-in restart can be added later if needed.

This replaces #6791, which is now closed; its testserver change and tests moved here.

How to review

  • First 2 commits: the re-land of Add libraries field to the clusters resource #6365, unchanged. Verify with git range-diff 76872b50a^..76872b50a 55f0c1626..1a0467ad3. Apart from the rewritten commit message and one surrounding line in RemapState that changed on main, the only difference is the PR number in the changelog fragment.
  • Remaining commits: the fix. Only these need a real review:
    1. Stop restarting clusters on library changes
    2. Testserver: install job task libraries on the existing cluster
    3. libraries-scenarios acceptance test (local only)
    4. interactive_cluster_declared_libs cloud test
    5. Read cluster libraries only when the config declares them (DoReadWithState, superseded by 7)
    6. Test updates for unmanaged libraries
    7. Skip undeclared cluster libraries in OverrideChangeDesc (replaces the DoReadWithState framework method from 5, per review)
    8. libraries-scenarios update for the unmanaged skip
    9. Remove cluster restart leftovers: the edited gate around waitForInstall (it now always runs after reconciling), restart comments, and the testserver clusters/restart handler
    10. Drop restart assertions from tests; rename libraries-restart to libraries-update

integration_whl/interactive_cluster is back to its original script and output: it declares no cluster libraries, so the redeploy after a job run is 2 unchanged on both engines, as before #6365.

Tests

  • New unit test TestClusterOverrideChangeDescLibraries: no section and removed section are unmanaged; libraries: [], declared libraries and per-library removals stay managed.
  • New acceptance/bundle/resources/clusters/libraries-scenarios (local): no section (out-of-band library skipped as unmanaged), then declare, drift and remove the section alongside an out-of-band library. It covers the READPLAN path.
  • New acceptance/bundle/integration_whl/interactive_cluster_declared_libs (Cloud + CloudSlow, both DMS variants): cluster-declared libraries plus a wheel job on the same cluster.
  • resources/clusters/libraries-update (renamed from libraries-restart, Cloud + CloudSlow): checks the install request on create and when adding a library, and the uninstall request for libraries: [].
  • The testserver no longer serves clusters/restart, so an accidental restart call fails the tests locally instead of needing explicit "no restart" checks.
  • Unit tests, lint and the full local acceptance suite pass. The full integration_whl tree and resources/clusters/libraries* pass on AWS cloud (run before commits 9–10).

This pull request and its description were written by Isaac.

@Sankalp-Mittal
Sankalp-Mittal requested review from a team as code owners September 24, 2026 09:41
@Sankalp-Mittal
Sankalp-Mittal added this pull request to stack #6832 September 24, 2026 09:43
@Sankalp-Mittal
Sankalp-Mittal marked this pull request as draft September 24, 2026 09:45
@github-actions github-actions Bot added DABs DABs related issues PyDABs labels Sep 24, 2026
@Sankalp-Mittal
Sankalp-Mittal marked this pull request as ready for review September 24, 2026 09:48
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 30c009e

Run: 36415551140

Env ✅​pass 🙈​skip Time
✅​ aws linux 347 368 17:08
✅​ aws windows 349 366 24:52
✅​ azure linux 346 368 16:26
✅​ azure windows 348 366 20:53
✅​ gcp linux 347 368 14:56
✅​ gcp windows 349 366 22:15
Top 50 slowest tests (at least 2 minutes):
duration env testname
10:27 aws windows TestAccept
9:31 gcp windows TestAccept
8:29 azure windows TestAccept
6:58 aws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
6:51 azure linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
4:25 aws linux TestAccept
4:13 gcp linux TestAccept
4:09 azure linux TestAccept
3:49 aws windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
3:33 gcp windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
3:26 aws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
3:12 gcp linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
3:11 gcp windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
3:02 gcp windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
3:01 gcp windows TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
3:00 gcp windows TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:57 gcp linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:51 gcp linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:50 aws windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:49 aws windows TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:47 gcp linux TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:46 azure windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:45 azure windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:45 gcp windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:37 gcp windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:37 gcp windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:36 gcp linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:27 gcp linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:27 aws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:21 aws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:21 aws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:20 gcp linux TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:20 azure windows TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:20 gcp linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:17 aws linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:17 azure linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:16 azure windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:14 azure linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:13 aws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:13 azure linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:13 azure linux TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:11 azure windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:10 aws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:10 aws windows TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:07 aws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:07 aws linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:06 azure windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:05 azure linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1
2:05 azure linux TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
2:05 aws linux TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=1

Base automatically changed from sankalp-mittal/revert-6365 to main September 24, 2026 10:29
Sankalp-Mittal and others added 2 commits September 24, 2026 10:29
Re-lands #6365, which was reverted so the cluster libraries feature can be fixed before it ships.

This reverts commit 3862ef8 (the revert of 76872b5).

Co-authored-by: Isaac <no-reply@databricks.com>
@Sankalp-Mittal
Sankalp-Mittal force-pushed the sankalp-mittal/cluster-libraries branch from e3ba12e to 1a0467a Compare September 24, 2026 10:29
Sankalp-Mittal and others added 4 commits September 24, 2026 12:59
Restarting an all-purpose cluster kills attached sessions and running work,
which is too disruptive to trigger implicitly on deploy. Installs apply live
on a running cluster; uninstalls are marked UNINSTALL_ON_RESTART and take
effect at the cluster's next restart.

Co-authored-by: Isaac <no-reply@databricks.com>
The Jobs service installs a task's libraries on its existing all-purpose
cluster via the Libraries API when a run starts, and they persist there.
Mirror that so cluster-status reports them locally, like on cloud. This
makes the integration_whl/interactive_* regression from #6365 reproduce
without a real workspace.

The direct engine now reads the job's wheel as cluster drift on redeploy,
so interactive_cluster records its second deploy per engine.

Co-authored-by: Isaac <no-reply@databricks.com>
Covers declaring, drifting and removing cluster libraries alongside an
out-of-band library, and asserts that no change restarts the cluster.

Co-authored-by: Isaac <no-reply@databricks.com>
The cluster declares libraries and a wheel job targets it via
existing_cluster_id. Removing a declared library uninstalls it together
with the job's libraries and does not restart the cluster.

Co-authored-by: Isaac <no-reply@databricks.com>
Comment thread acceptance/bundle/integration_whl/interactive_cluster/script Outdated
Sankalp-Mittal and others added 2 commits September 24, 2026 14:16
A cluster without a libraries section now behaves as before the field
existed: the plan does not read the cluster's libraries, so libraries
installed by job runs or out of band are not drift, and no library calls
are made. Removing the section stops managing the libraries and leaves
them installed; `libraries: []` still uninstalls them.

To make the read depend on the config, add an optional DoReadWithState
resource method that receives the node's local state. Planning uses it
when implemented; reads without a local state (deletes, the post-apply
refresh for remote references) keep using DoRead.

Co-authored-by: Isaac <no-reply@databricks.com>
interactive_cluster declares no cluster libraries, so it is back to its
original output on both engines. libraries-scenarios checks that the plan
reads no libraries without a section and that removing the section makes
no library calls. libraries-restart empties the list instead of dropping
it, so it still exercises an uninstall.

Co-authored-by: Isaac <no-reply@databricks.com>
Comment thread bundle/direct/dresources/cluster.go Outdated
Replace the DoReadWithState framework method from 5d0fe69 with a
cluster-only override, as suggested in review. The cluster reads its
libraries unconditionally again, and when the config has no libraries
section the whole-field change is skipped with the new reason
`unmanaged`: no drift, no "Updated clusters.*" line, and no install or
uninstall calls. Removing the section also leaves the libraries
installed and unmanaged, while `libraries: []` stays managed.

config-remote-sync ignores skips other than remote_addition, so
unmanaged libraries are not synced into the config.

Co-authored-by: Isaac <no-reply@databricks.com>
The plan now reads the cluster's libraries without a section and skips
them with reason `unmanaged`, so the step records the skipped change
instead of asserting that no library read happens.

Co-authored-by: Isaac <no-reply@databricks.com>
andrewnester
andrewnester previously approved these changes Sep 28, 2026
@andrewnester
andrewnester self-requested a review September 28, 2026 09:45
@Sankalp-Mittal
Sankalp-Mittal added this pull request to the merge queue Sep 28, 2026
Comment thread bundle/direct/dresources/cluster.go Outdated
// and running work. Installs apply live on a running cluster; uninstalls are marked
// UNINSTALL_ON_RESTART and take effect at the cluster's next restart.
// A cluster edit restarts the cluster on its own, so only wait when there was no edit.
if !edited {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to do it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a remanant from the old restart design I'll refactor the code to remove all of these

@andrewnester
andrewnester removed this pull request from the merge queue due to a manual request Sep 28, 2026
@andrewnester
andrewnester self-requested a review September 28, 2026 09:50
@andrewnester
andrewnester dismissed their stale review September 28, 2026 09:51

reviewed too fast :)

Comment thread bundle/direct/dresources/cluster.go
Comment thread bundle/direct/dresources/cluster.go

@andrewnester andrewnester left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, please clean up the code from old restart logic before merging

Sankalp-Mittal and others added 2 commits September 28, 2026 11:24
The bundle no longer restarts clusters for library changes, so drop what
only existed for that design: the `edited` gate that skipped waiting for
installs when a cluster edit restarted the cluster, the restart
reasoning in the DoUpdate and WaitAfterCreate comments, and the
testserver's clusters/restart handler. waitForInstall now always runs
after reconciling; it already returns early when the cluster is not
running.

Co-authored-by: Isaac <no-reply@databricks.com>
Rename libraries-restart to libraries-update and check the install and
uninstall requests instead of restart requests. Remove the
//clusters/restart checks, the "!Restarting cluster" checks for a log
line that no longer exists, and the "no restart" wording elsewhere.
With the testserver restart route gone, an accidental restart call now
fails the tests locally.

Co-authored-by: Isaac <no-reply@databricks.com>
@Sankalp-Mittal
Sankalp-Mittal added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 3a60aef Sep 28, 2026
31 checks passed
@Sankalp-Mittal
Sankalp-Mittal deleted the sankalp-mittal/cluster-libraries branch September 28, 2026 12:31
deco-sdk-tagging Bot added a commit that referenced this pull request Sep 30, 2026
## Release v1.19.0

### CLI

 * Honor `CLAUDE_CONFIG_DIR` in `aitools` commands. ([#6838](#6838))
 * Added `--ttl` and `--no-expiry` flags to `databricks postgres create-branch` so a branch's expiration can be set without hand-writing a `--json` spec. `--ttl` accepts the REST API duration form (`604800s`), a Go duration (`168h`), or day/week units (`7d`, `3w`); `--no-expiry` creates a branch that never expires. One of `--ttl`, `--no-expiry`, or a spec expiration in `--json` is required. ([#6313](#6313))
 * `databricks ssh connect` serverless sessions now provide Claude Code and Codex configured with Unity Gateway out of the box. ([#6885](#6885))

### AI Runtime

 * Add `databricks air images push` (Preview) to configure Docker authentication and push container images to Databricks Artifact Registry. ([#6869](#6869))
 * `air run` now grants the configured `permissions` on the MLflow experiment as well as the job. ([#6870](#6870))

### Bundles

 * Add libraries field to clusters. ([#6831](#6831))
 * Error out when a configured `workspace_id` does not match the connected workspace, instead of silently using it in resource URLs emitted by `bundle summary`. ([#6754](#6754))
 * Fix spurious recreation of Lakebase (Postgres) branches, roles, and catalogs when the referenced project is updated in place: an in-place project change (e.g. `display_name`) no longer forces a delete + create of resources that reference the project's or branch's `name`. ([#6865](#6865))
 * Direct engine now detects and applies an explicitly configured integer zero (e.g. `gcp_attributes.local_ssd_count: 0`) added to a resource first deployed without the field. ([#6867](#6867))
 * Migrate existing Terraform deployment state to the direct engine before deploying (previously done after a Terraform deploy), so the deploy runs on the direct engine. ([#6749](#6749))
 * The `postgres_snapshot_schedules` resource (introduced in [v1.16.0](https://github.com/databricks/cli/releases/tag/v1.16.0)) is now marked Beta and is no longer available in PyDABs, matching the other `postgres_*` resources; configure it in YAML instead. ([#6887](#6887))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DABs DABs related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: configure all-purpose cluster libraries through DAB

3 participants