Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nextchanges/bundles/immutable-bind.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Fix `bundle deployment bind` (and `bundle generate --bind`) failing with `no such node "resources.internal_immutable_snapshots.immutable"` when `workspace.sync_mode` is `immutable`, as reported in [#7306](https://github.com/databricks/cli/issues/7306).
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
bundle:
name: test-bundle-$UNIQUE_NAME

workspace:
sync_mode: immutable

resources:
jobs:
foo:
name: test-job-bind-$UNIQUE_NAME
tasks:
- task_key: my_task
environment_key: default
spark_python_task:
python_file: ./hello_world.py
environments:
- environment_key: default
spec:
environment_version: "4"
# References no bundle files. Binding it still plans the whole bundle, so the
# snapshot that the job above depends on has to resolve even here.
schemas:
schema1:
name: $SCHEMA_NAME
catalog_name: main
comment: This schema was created from DABs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("Hello, World!")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Cloud = true
EnvMatrix.DMS = [""]
52 changes: 52 additions & 0 deletions acceptance/bundle/deployment/bind/job/immutable/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

=== Create a pre-defined job and schema:
Created job with ID: [JOB_ID]
{
"full_name": "main.test-schema-[UNIQUE_NAME]",
"catalog_name": "main"
}

=== Bind the job while the state holds no snapshot yet:
>>> [CLI] bundle deployment bind foo [JOB_ID] --auto-approve
Successfully bound job with an id '[JOB_ID]'
Run 'bundle deploy' to deploy changes to your workspace

>>> [CLI] bundle plan
create internal_immutable_snapshots.immutable
update jobs.foo
create schemas.schema1

Plan: 2 to add, 1 to change, 0 to delete, 0 unchanged

=== Deploy the job, which deploys the snapshot it depends on:
>>> [CLI] bundle deploy --select jobs.foo
Created internal_immutable_snapshots.immutable
Updated jobs.foo
Files: 0 uploaded, 0 deleted
Resources: 1 created, 1 changed, 0 deleted, 0 unchanged, 1 not selected

>>> [CLI] jobs get [JOB_ID]
"/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/hello_world.py"

=== Bind the schema now that the state holds a snapshot the config does not declare:
>>> [CLI] bundle deployment bind schema1 main.test-schema-[UNIQUE_NAME] --auto-approve
Successfully bound schema with an id 'main.test-schema-[UNIQUE_NAME]'
Run 'bundle deploy' to deploy changes to your workspace

>>> [CLI] bundle plan
update schemas.schema1

Plan: 0 to add, 1 to change, 0 to delete, 2 unchanged

=== Unbind both and destroy the bundle:
>>> [CLI] bundle deployment unbind foo

>>> [CLI] bundle deployment unbind schema1

>>> [CLI] bundle destroy --auto-approve
All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test-bundle-[UNIQUE_NAME]/default

Destroy: 0 deleted

=== Delete the pre-defined job and schema (both must have survived the destroy):0
0
58 changes: 58 additions & 0 deletions acceptance/bundle/deployment/bind/job/immutable/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
title "Create a pre-defined job and schema:\n"

PYTHON_FILE="/Workspace/Users/${CURRENT_USER_NAME}/initial_hello_world.py"

JOB_ID=$($CLI jobs create --json '
{
"name": "test-job-bind-'${UNIQUE_NAME}'",
"tasks": [
{
"task_key": "my_task",
"environment_key": "default",
"spark_python_task": {
"python_file": "'${PYTHON_FILE}'"
}
}
],
"environments": [
{
"environment_key": "default",
"spec": {
"environment_version": "4"
}
}
]
}' | jq -r '.job_id')
add_repl "$JOB_ID" JOB_ID
echo "Created job with ID: $JOB_ID"

export SCHEMA_NAME="test-schema-${UNIQUE_NAME}"
$CLI schemas create ${SCHEMA_NAME} main | jq '{full_name, catalog_name}'

envsubst < databricks.yml.tmpl > databricks.yml

cleanup() {
title "Delete the pre-defined job and schema (both must have survived the destroy):"
$CLI jobs delete $JOB_ID
echo $?
$CLI schemas delete main.${SCHEMA_NAME}
echo $?
}
trap cleanup EXIT

title "Bind the job while the state holds no snapshot yet:"
trace $CLI bundle deployment bind foo $JOB_ID --auto-approve
trace $CLI bundle plan

title "Deploy the job, which deploys the snapshot it depends on:"
trace $CLI bundle deploy --select jobs.foo
trace $CLI jobs get $JOB_ID | jq '.settings.tasks[0].spark_python_task.python_file'

title "Bind the schema now that the state holds a snapshot the config does not declare:"
trace $CLI bundle deployment bind schema1 main.${SCHEMA_NAME} --auto-approve
trace $CLI bundle plan

title "Unbind both and destroy the bundle:"
trace $CLI bundle deployment unbind foo
trace $CLI bundle deployment unbind schema1
trace $CLI bundle destroy --auto-approve
12 changes: 12 additions & 0 deletions acceptance/bundle/deployment/bind/job/immutable/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DMS does not support the immutable snapshot resource yet (see bundle/deploy/immutable/test.toml).
EnvMatrix.DMS = [""]

Ignore = [
"databricks.yml",
".databricks",
]

[[Repls]]
# Snapshot paths embed the bundle ID and the content hash, both sha256 hex digests.
Old = "[0-9a-f]{64}"
New = "[SNAPSHOT_HASH]"
12 changes: 12 additions & 0 deletions bundle/phases/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config/engine"
"github.com/databricks/cli/bundle/deploy/lock"
"github.com/databricks/cli/bundle/deploy/snapshot"
"github.com/databricks/cli/bundle/deployplan"
"github.com/databricks/cli/bundle/statemgmt"
"github.com/databricks/cli/libs/agent"
Expand Down Expand Up @@ -47,6 +48,17 @@ func Bind(ctx context.Context, b *bundle.Bundle, opts *BindOptions, stateDesc *s
return
}

// In immutable sync mode, translated paths reference the snapshot (an internal resource,
// see resources.SnapshotFullPathRef) that only snapshot.PlanUpload registers in the config.
// The plans below cannot resolve those references without it, so register it as plan and
// deploy do.
if b.IsImmutableFolder() {
bundle.ApplyContext(ctx, b, snapshot.PlanUpload(snapshot.PlanUploadOptions{Clean: true}))
if logdiag.HasError(ctx) {
return
}
}

// Direct engine: import into temp state, run plan, check for changes.
resourceKey := fmt.Sprintf("resources.%s.%s", opts.Group, opts.ResourceKey)
_, statePath := b.StateFilenameDirect(ctx)
Expand Down
Loading