Skip to content

internal: Refactor immutable folder upload as an internal resource in direct engine - #6084

Open
andrewnester wants to merge 36 commits into
mainfrom
feat/upload-resource
Open

internal: Refactor immutable folder upload as an internal resource in direct engine#6084
andrewnester wants to merge 36 commits into
mainfrom
feat/upload-resource

Conversation

@andrewnester

Copy link
Copy Markdown
Contributor

Changes

Refactor immutable folder upload as an internal resource in direct engine

Why

This enables us:

  1. Keep track of upload state and upload paths, so next deployment can check the status of the uploaded folder. Needed for breakglass
  2. Easier path resolution: now all resources can just reference to internal resource like ${internal.snapshot.full_path} and it's automatically resolved during deploy
  3. Plan is correct now and can easily skip uploads if nothing changed and no need to upload

Tests

Existing tests pass

@andrewnester
andrewnester requested a review from denik July 28, 2026 11:29
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/bundle/ - needs approval

18 files changed
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @shreyas-goenka, @lennartkats-db, @anton-107

/bundle/ - needs approval

33 files changed
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @shreyas-goenka, @lennartkats-db, @anton-107

/cmd/bundle/ - needs approval

Files: cmd/bundle/plan.go
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @shreyas-goenka, @lennartkats-db, @anton-107

/libs/sync/ - needs approval

Files: libs/sync/path.go
Suggested: @simonfaltum
Also eligible: @Divyansh-db, @renaudhartert-db, @tanmay-db, @hectorcast-db, @parthban-db, @tejaskochar-db, @mihaimitrea-db, @chrisst, @rauchy

General files (require maintainer)

4 files changed
Based on git history:

  • @denik -- recent work in bundle/phases/, bundle/direct/dresources/, bundle/direct/

Any maintainer (@anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db, @rugpanov, @rclarey) can approve all areas.
See OWNERS for ownership rules.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: b60469f

Run: 32761216334

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 1 4 276 1167 7:46
💚​ aws windows 1 4 278 1165 6:36
💚​ azure linux 1 4 275 1167 9:58
🔄​ azure windows 1 1 4 276 1165 6:50
💚​ gcp linux 1 4 276 1167 9:37
💚​ gcp windows 1 4 278 1165 7:52
6 interesting tests: 4 SKIP, 1 RECOVERED, 1 flaky
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestSyncIncrementalSyncFileToPythonNotebook ✅​p ✅​p ✅​p 🔄​f ✅​p ✅​p
Top 8 slowest tests (at least 2 minutes):
duration env testname
3:14 gcp windows TestAccept
3:14 aws windows TestAccept
3:09 azure windows TestAccept
2:44 azure linux TestFilerWorkspaceFilesExtensionsReadDir
2:43 gcp linux TestAccept/bundle/resources/genie_spaces/recreate_when_gone/DATABRICKS_BUNDLE_ENGINE=direct
2:42 gcp windows TestFilerRecursiveDelete/workspace_files
2:07 gcp windows TestFilerWorkspaceFilesExtensionsRead
2:02 gcp linux TestFilerWorkspaceFilesExtensionsDelete

@denik denik 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.

I think we should not have edge case for "internal" resources. We can just create resources.internal_files, populate it ourselves (forbid users from touching it) and then just let the framework handle it as a regular resource.

Comment thread acceptance/bundle/deploy/immutable-no-artifacts/output.txt Outdated
Comment thread bundle/direct/bundle_plan.go Outdated
"deployments": [
{
"command_path": "${workspace.snapshot_path}/files/src/main.py",
"command_path": "${internal.snapshot.full_path}/files/src/main.py",

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.

Instead of custom path, can we keep using ${workspace.file_path}, just make it an alias internally to internal resource's snapshot path.

That way:

  • users know what this means
  • users can use the variable themselves and it'll work both in regular and immutable case.

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.

${workspace.file_path} is ${internal.snapshot.full_path}/files and because we resolve the variables ${workspace.file_path} resolved to this pass. I'd prefer not to change the way variable reslution works and introduce some custom aliases

Comment thread bundle/direct/dresources/adapter.go Outdated
@andrewnester
andrewnester requested a review from denik August 3, 2026 13:00
Comment thread bundle/config/mutator/reject_internal_resources.go Outdated
Comment thread bundle/config/validate/files_to_sync.go Outdated
Comment thread libs/snapshot/client.go Outdated
@@ -35,12 +36,15 @@ type ACLEntry struct {
// the same workspace location.
// This interface exists so the implementation can later be replaced with a Go SDK call.

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.

This does not explain why we need the interface - do we plan on supporting multiple implementation at the same time?

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.

Strictly speaking we don't need it for SDK replacement later but we need this now in unit tests to replace the uploader with mock one

Comment thread acceptance/bundle/deploy/immutable-no-artifacts/script Outdated
Comment thread acceptance/bundle/deploy/immutable-no-artifacts/script Outdated
Comment thread acceptance/bundle/deploy/immutable-no-artifacts/output.txt Outdated
@andrewnester
andrewnester requested a review from denik August 4, 2026 09:11
Comment thread libs/snapshot/client.go Outdated
Comment thread acceptance/bundle/deploy/immutable-no-artifacts/out.test.toml
Comment thread acceptance/bundle/refschema/out.fields.txt Outdated
Comment thread bundle/config/resources/snapshot.go Outdated
Comment thread bundle/config/resources/snapshot.go Outdated
Comment thread bundle/direct/dresources/resources.yml
@andrewnester
andrewnester requested a review from denik August 5, 2026 11:16
@pietern
pietern self-requested a review August 11, 2026 11:29
Comment thread bundle/config/mutator/translate_paths.go
Comment thread bundle/deploy/snapshot/upload.go
Comment thread libs/snapshot/client.go
Comment thread bundle/direct/bundle_plan.go Outdated
Comment thread acceptance/bundle/resources/apps/immutable/output.txt Outdated
Comment thread bundle/config/resources/snapshot.go Outdated
Comment thread bundle/deploy/snapshot/upload.go Outdated
Comment thread bundle/phases/destroy.go Outdated
All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/recreate-gone-genie-space-[UNIQUE_NAME]/default

Destroy: 1 deleted
Destroy: 0 deleted

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.

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.

Could have been a separate PR if unrelated to this work.

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default

Destroy: 1 deleted
Destroy: 0 deleted

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.

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default

Destroy: 1 deleted
Destroy: 0 deleted

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.

Comment thread bundle/run/app.go
// references resolved against the current bundle state. This is needed for immutable
// folder bundles where source_code_path contains a reference to the snapshot's
// full_path, which is only known after deploy.
func (a *appRunner) resolvedSourceCodePath() (string, error) {

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.

This routine is a point solution for getting the fully resolved deployment state.

It feels like this belongs on a generic function that returns the state. Then the source_code_path will be pre-resolved and we don't need the check above at all; the path is usable as is.

tag := resources.Type().Field(i).Tag.Get("bundle")
if tag == "internal" {
continue
}

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.

The comment should explain why this is ignoring internal resources (I can't tell why).

Comment thread bundle/phases/deploy.go
// mutators need informed consent if they are potentially destructive.
bundle.ApplySeqContext(ctx, b,
bundle.ApplySeqContext(
ctx, b,

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.

This and the above diffs are nops.

Comment thread bundle/libraries/filer.go
uploadPath := path.Join(b.Config.Workspace.ArtifactPath, InternalDirName)
uploadPath = ensureWorkspaceOrVolumesPrefix(uploadPath)
if b.IsImmutableFolder() {
uploadPath = path.Join("${resources.internal_immutable_snapshots.immutable.full_path}", "artifacts", InternalDirName)

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.

The static variable ref should be a const.

if targetPath == "workspace.snapshot_path" {
if targetNode == "" {
continue
}

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.

Is this empty check still needed?

// Skip fields configured in ignore_remote_changes.
if ignoreFilter.shouldIgnore(path.String()) {
return
}

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 was the move needed? Wondering if there is anything structural missing that makes the above code not work without the ignore check.

Comment thread bundle/deploy/snapshot/upload.go
Comment thread bundle/deploy/snapshot/upload.go Outdated
Secrets map[string]*resources.Secret `json:"secrets,omitempty"`

// Internal resources
Snapshots map[string]*resources.Snapshot `json:"internal_immutable_snapshots,omitempty" bundle:"internal"`

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.

WDYT about prefixing this with a _ to make it clearer it is internal just by looking at the state.

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/recreate-gone-genie-space-[UNIQUE_NAME]/default

Destroy: 1 deleted
Destroy: 0 deleted

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.

Could have been a separate PR if unrelated to this work.


>>> [CLI] bundle deploy
Uploading immutable bundle snapshot...
Created internal_immutable_snapshots.immutable

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.

This should show up differently in the deploy output.

Comment thread bundle/deploy/snapshot/upload.go Outdated
Comment thread bundle/libraries/filer.go
artifactPath = ensureWorkspaceOrVolumesPrefix(artifactPath)
if b.IsImmutableFolder() {
artifactPath = path.Join("${resources.internal_immutable_snapshots.immutable.full_path}", "artifacts")
}

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.

Cleanup is not done for immutable folders.

Separate note: should we write back the above artifactPath to workspace.artifact_path?

This is done already as a default in bundle/config/mutator/default_workspace_paths.go.

// Reads (dynamic): resources.internal_immutable_snapshots (and other internal keys)
// Warns and removes any internal resource fields set by the user.
mutator.RejectInternalResources(),

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.

Nit: not a dynamic but a typed read.

Comment thread bundle/deploy/snapshot/upload.go Outdated
// into the in-memory state cache entry for the snapshot resource. This is needed when
// deploying from a plan file: the plan JSON omits ZipContent (json:"-"), so InitForApply
// leaves it empty, causing DoCreate to upload an empty zip and derive a wrong snapshot ID.
func SyncZipContent(b *bundle.Bundle) {

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.

Shouldn't this return an error on the !ok cases below?


func (s *ResourceSnapshot) DoDelete(ctx context.Context, id string, state *SnapshotState) error {
return nil
}

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.

I believe the adapter can deal with methods that don't apply being unspecified.

Comment thread bundle/direct/dresources/snapshot.go Outdated
type SnapshotState struct {
RemoteRoot string `json:"remote_root"`
RelativePath string `json:"relative_path"`
FullPath string `json:"full_path"`

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.

Are RelativePath and FullPath refer to remote location? Why don't they have Remote prefix like RemoteRoot?

Can you add comments and example values for these fields? It feels like there is some redundancy between RemoteRoot, RelativePath, FullPath but I cannot tell for sure because I don't know what these mean exactly.

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.

5 participants