Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
bundle:
name: test-bundle-$UNIQUE_NAME

# A target name may contain a dot. The write path to an override block is
# targets.<target>.<field>, which is parsed back into segments, so a dotted name has to
# be quoted or "dev.eu" becomes two keys and addresses a node that does not exist.
resources:
jobs:
dotted_job:
tasks:
- task_key: main
max_retries: 1
notebook_task:
notebook_path: /Users/{{workspace_user_name}}/nb

targets:
dev.eu:
mode: development
resources:
jobs:
dotted_job:
tasks:
- task_key: main
timeout_seconds: 45

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle-[UNIQUE_NAME]/dev.eu/files...
Deploying resources...
Updating deployment state...
Deployment complete!

=== Edit a field in the override block of a target whose name contains a dot
=== Sync
Detected changes in 1 resource(s):

Resource: resources.jobs.dotted_job
tasks[task_key='main'].timeout_seconds: replace



=== The override block is updated

>>> diff.py databricks.yml.backup databricks.yml
--- databricks.yml.backup
+++ databricks.yml
@@ -22,3 +22,3 @@
tasks:
- task_key: main
- timeout_seconds: 45
+ timeout_seconds: 900

>>> [CLI] bundle destroy --auto-approve -t dev.eu
The following resources will be deleted:
delete resources.jobs.dotted_job

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

Deleting files...
Destroy complete!
22 changes: 22 additions & 0 deletions acceptance/bundle/config-remote-sync/split/dotted_target/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
envsubst < databricks.yml.tmpl > databricks.yml
cleanup() { trace $CLI bundle destroy --auto-approve -t dev.eu; }
trap cleanup EXIT
$CLI bundle deploy -t dev.eu
job_id="$(read_id.py -t dev.eu dotted_job)"

title "Edit a field in the override block of a target whose name contains a dot"
edit_resource.py jobs $job_id <<PYEDIT
for task in r["tasks"]:
if task["task_key"] == "main":
task["timeout_seconds"] = 900
PYEDIT

title "Sync"
echo
cp databricks.yml databricks.yml.backup
errcode $CLI bundle config-remote-sync -t dev.eu --save
title "The override block is updated"
echo
trace diff.py databricks.yml.backup databricks.yml
rm databricks.yml.backup
27 changes: 24 additions & 3 deletions bundle/configsync/blockindex.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,32 @@ func (r *blockResolver) regionPath(block sourceBlock, path dyn.Path) dyn.Path {

// candidatePath renders a resolved path the way the patch layer addresses it,
// which for an override block includes the targets.<target> prefix.
func (r *blockResolver) candidatePath(block sourceBlock, path string) string {
//
// The prefix is built as path nodes rather than concatenated as text: a target name
// may contain a dot ("dev.eu" is a legal name), and the result is parsed back into
// segments, so text would split one name into two keys and address the wrong node.
func (r *blockResolver) candidatePath(block sourceBlock, path *structpath.PatternNode) string {
if !block.override {
return path
return path.String()
}
return targetPrefixedPath(r.target, path)
}

// targetPrefixedPath prefixes path with targets.<target>, quoting the target name
// where necessary so the result parses back into the same segments.
func targetPrefixedPath(target string, path *structpath.PatternNode) string {
prefixed := structpath.NewPatternStringKey(nil, "targets")
prefixed = structpath.NewPatternStringKey(prefixed, target)
for _, node := range path.AsSlice() {
if key, ok := node.StringKey(); ok {
prefixed = structpath.NewPatternStringKey(prefixed, key)
} else if index, ok := node.Index(); ok {
prefixed = structpath.NewPatternIndex(prefixed, index)
} else {
prefixed = structpath.NewPatternBracketStar(prefixed)
}
}
return "targets." + r.target + "." + path
return prefixed.String()
}

// sortedBlocks lists the known blocks with the top-level ones first and a total
Expand Down
7 changes: 3 additions & 4 deletions bundle/configsync/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes
result = append(result, FieldChange{
FilePath: block.file,
Change: destChange,
FieldCandidates: []string{blocks.candidatePath(block, mergedPath)},
FieldCandidates: []string{blocks.candidatePath(block, resolvedPath)},
mergedPath: mergedPath,
})
continue
Expand Down Expand Up @@ -383,12 +383,11 @@ func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes
var candidates []string
if routed {
// The block is known, so there is exactly one path to write.
candidates = []string{blocks.candidatePath(block, resolvedPathStr)}
candidates = []string{blocks.candidatePath(block, resolvedPath)}
} else {
candidates = []string{resolvedPathStr}
if targetName != "" {
targetPrefixedPath := "targets." + targetName + "." + resolvedPathStr
candidates = append(candidates, targetPrefixedPath)
candidates = append(candidates, targetPrefixedPath(targetName, resolvedPath))
}
}

Expand Down
Loading