bundle/deploy/lock: introduce DeploymentManager interface aligned with DMS versioning model#5314
Open
shreyas-goenka wants to merge 12 commits into
Open
bundle/deploy/lock: introduce DeploymentManager interface aligned with DMS versioning model#5314shreyas-goenka wants to merge 12 commits into
shreyas-goenka wants to merge 12 commits into
Conversation
Pure code-movement refactor. Wraps the existing workspace-filesystem lock
behavior behind a DeploymentLock interface so a follow-up PR can introduce
an alternative metadata-service-backed lock implementation without
touching deploy/destroy/bind callers again.
What changed:
- New bundle/deploy/lock/lock.go: DeploymentLock interface, Goal enum
(moved from release.go), DeploymentStatus enum, and a
NewDeploymentLock factory that unconditionally returns the workspace
filesystem implementation.
- New bundle/deploy/lock/workspace_filesystem.go: workspaceFilesystemLock
struct that implements DeploymentLock. Preserves the historical
behavior of the deleted acquire.go / release.go mutators: lock-disabled
short-circuit, locker.CreateLocker initialization, the
permissions.ReportPossiblePermissionDenied branch on fs.ErrPermission
/ fs.ErrNotExist, and the destroy-mode locker.AllowLockFileNotExist
unlock quirk.
- Deleted bundle/deploy/lock/acquire.go and bundle/deploy/lock/release.go.
- Updated bundle/phases/{deploy,destroy,bind}.go to construct the lock
once via NewDeploymentLock and call Acquire / Release directly instead
of through bundle.ApplyContext. The deferred Release now reports
DeploymentSuccess / DeploymentFailure based on logdiag.HasError so a
future DMS-backed implementation can record the outcome.
Behavior is preserved end-to-end: lock-related acceptance goldens
(pipelines/{deploy,destroy}/force-lock, bundle/help/bundle-{deploy,
destroy}) all pass unchanged.
Collaborator
|
Commit: 715a18b |
This was referenced May 26, 2026
…dle.Bundle Pre-refactor the locker was stashed on b.Locker so the two mutators (acquire then release) could share it via the Bundle. After PR #5314 both lifecycle methods live on a single struct, so the cross-method state-passing through bundle.Bundle is redundant — grep finds zero external consumers of b.Locker. Move the *locker.Locker to a field on workspaceFilesystemLock and delete the now-unused Locker field on bundle.Bundle. Co-authored-by: Isaac
The struct now holds only the primitives + workspace client + a permission-error callback it needs. The bundle-aware wiring lives in NewDeploymentLock, which captures everything from the *bundle.Bundle at construction time. Why: keeps the type-level dependency surface narrow (the impl no longer imports bundle or bundle/permissions), removes a class of accidental coupling that would make alternative lock implementations awkward, and forecloses the possibility of a future bundle <-> lock import cycle. NewDeploymentLock now takes ctx so it can call b.WorkspaceClient(ctx) at construction; the three callers in bundle/phases are updated in one line each. Co-authored-by: Isaac
The lock and workspace_filesystem files live in the same package, so the package-level import of bundle/permissions exists regardless of whether the struct hides the call behind a function value. The callback only added a layer of indirection; switch to a direct permissions.ReportPossiblePermissionDenied call and keep a narrow b field purely for that error path. Co-authored-by: Isaac
The error is returned to the caller; logging it here just produces a duplicate line in the user-facing output. Drop the log; preserve the permission-denied branch and the bare return. Co-authored-by: Isaac
…or semantics) The original release.go mutator returned diag.FromErr on unlock failure, which surfaced as an error diagnostic to the user. The defer pattern introduced in #5314 dropped it to log.Warnf — a silent demotion that would hide a stuck lock from the user (who normally has to recover with --force-lock). Switch the defer to logdiag.LogError so the unlock failure shows up as a proper diagnostic, matching the pre-refactor behavior. The deploy/destroy/ bind/unbind phases all share the same fix. Co-authored-by: Isaac
shreyas-goenka
commented
May 28, 2026
|
|
||
| defer func() { | ||
| bundle.ApplyContext(ctx, b, lock.Release(lock.GoalBind)) | ||
| status := lock.DeploymentSuccess |
Contributor
Author
There was a problem hiding this comment.
status is not used for now - but will be used with deployment metadata service.
…lesystemLock ReportPossiblePermissionDenied has a second caller (bundle/deploy/files/ upload.go) and walks bundle.Config.RunAs / Permissions / Resources via analyzeBundlePermissions, so the lock can't simply inline it or call a simplified variant. But the struct doesn't need to pin a *bundle.Bundle field — the bundle-aware wiring can live in a callback closure that NewDeploymentLock builds at construction time. After this commit workspaceFilesystemLock holds only primitives, a workspace client, and the callback; no bundle types appear in its field list. Co-authored-by: Isaac
…ment lock" goldens Earlier in this PR we removed the log.Errorf wrapper in workspaceFilesystemLock because the same error is already surfaced via the returned diag. That removed the duplicate "Error: Failed to acquire deployment lock: ..." line from the CLI's stderr output, which two force-lock acceptance goldens were still expecting. Update both goldens to match the single-line shape. Co-authored-by: Isaac
The log.Errorf in workspaceFilesystemLock.Acquire isn't redundant with the downstream logdiag.LogError — it emits a separate user-visible "Error: Failed to acquire deployment lock: ..." stderr line that the force-lock acceptance goldens depend on. Removing it (commit 0433a83) was a real behavior change; this PR is supposed to be a pure refactor. Restore the log call and the two golden files. Co-authored-by: Isaac
Contributor
Waiting for approvalBased on git history, these people are best suited to review:
Eligible reviewers: Suggestions based on git history. See OWNERS for ownership rules. |
The original acquire.go mutator never called b.WorkspaceClient(ctx) when lock.enabled was false — the disabled check returned early in Apply before init() ran. After moving primitives to construction time the client was being eagerly initialized regardless, which adds a network call for the common dev-mode case where locking is disabled. Gate the client init on the captured enabled flag. Co-authored-by: Isaac
denik
reviewed
May 29, 2026
…oseVersion Renames the DeploymentLock interface to DeploymentManager and replaces Acquire/Release with CreateVersion/CloseVersion to align with the Deployment Metadata Service (DMS) data model, where deployments are versioned operations rather than mutual-exclusion locks. - CreateVersion(ctx, goal) (int64, error): begins a deployment; the workspace-filesystem implementation acquires the lock file and returns version 0 as a placeholder. DMS will return the atomically assigned version number (+1 to latest closed version). - CloseVersion(ctx, version, status): ends the deployment; the workspace-filesystem implementation releases the lock file. The goal moves from the NewDeploymentManager constructor to CreateVersion so a single manager instance is decoupled from a specific operation type. Co-authored-by: Shreyas Goenka <shreyas.goenka@databricks.com>
Co-authored-by: Shreyas Goenka <shreyas.goenka@databricks.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a
DeploymentManagerinterface that abstracts deployment locking and versioning, shaped around DMS (Deployment Management Service) versioning semantics.Interface:
CreateVersionwill atomically succeed only if the new version is +1 relative to the latest — this is the DMS contract. The workspace-filesystem implementation acquires the workspace lock file and returns version0as a placeholder (since the lock file doesn't track versions).CompleteVersionrecords the outcome of a deployment and releases the lock. The workspace-filesystem implementation releases the lock file.Factory:
Goal is not passed to the constructor — it moves to
CreateVersionto match the DMS versioning model.Files deleted:
acquire.go,release.go(logic folded intoworkspaceFilesystemLock).Updated callers:
bundle/phases/deploy.go,bundle/phases/destroy.go,bundle/phases/bind.goall use the new interface.This interface is a no-op shim today; a follow-on PR will wire in a real DMS client when
DATABRICKS_BUNDLE_USE_CLIENT_VERSIONINGis set.This pull request and its description were written by Isaac.