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
20 changes: 20 additions & 0 deletions acceptance/bundle/validate/duplicate_yaml_merge_key/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
bundle:
name: test-bundle

definitions:
cluster1: &cluster1
num_workers: 1
cluster2: &cluster2
spark_version: "13.3.x-scala2.12"

resources:
jobs:
my_job:
name: "test job"
tasks:
- task_key: "main"
new_cluster:
<<: *cluster1
<<: *cluster2
notebook_task:
notebook_path: "/notebook"

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

20 changes: 20 additions & 0 deletions acceptance/bundle/validate/duplicate_yaml_merge_key/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

>>> [CLI] bundle validate
Error: duplicate YAML merge key ('<<') is not allowed; to merge multiple maps, use a sequence: '<<: [*anchor1, *anchor2]'
in databricks.yml:18:13


Found 1 error

>>> [CLI] bundle validate
Name: test-bundle
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default

Validation OK!
{
"num_workers": 1,
"spark_version": "13.3.x-scala2.12"
}
8 changes: 8 additions & 0 deletions acceptance/bundle/validate/duplicate_yaml_merge_key/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
musterr trace $CLI bundle validate

update_file.py databricks.yml " <<: *cluster1
<<: *cluster2" " <<: [*cluster1, *cluster2]"

trace $CLI bundle validate

$CLI bundle validate -o json | jq '.resources.jobs.my_job.tasks[0].new_cluster'
9 changes: 9 additions & 0 deletions bundle/config/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"reflect"
Expand Down Expand Up @@ -106,6 +107,14 @@ func LoadFromBytes(path string, raw []byte) (*Root, diag.Diagnostics) {
// Load configuration tree from YAML.
v, err := yamlloader.LoadYAML(path, bytes.NewBuffer(raw))
if err != nil {
var le *yamlloader.LocationError
if errors.As(err, &le) {
return nil, diag.Diagnostics{{
Severity: diag.Error,
Summary: le.Summary,
Locations: []dyn.Location{le.Loc},
}}
}
return nil, diag.Errorf("failed to load %s: %v", path, err)
}

Expand Down
18 changes: 17 additions & 1 deletion libs/dyn/yamlloader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ import (
"go.yaml.in/yaml/v3"
)

// LocationError is an error with a YAML source location that can be displayed
// to the user with a file path, line, and column number.
type LocationError struct {
Loc dyn.Location
Summary string
}

func (e *LocationError) Error() string {
return fmt.Sprintf("yaml (%s): %s", e.Loc, e.Summary)
}

type loader struct {
path string
}
Expand Down Expand Up @@ -110,7 +121,12 @@ func (d *loader) loadMapping(node *yaml.Node, loc dyn.Location) (dyn.Value, erro
// However, when used as a key, it is treated as the string "null".
case "!!merge":
if merge != nil {
panic("merge node already set")
// The YAML merge key spec allows a single '<<' key per mapping.
// To merge multiple maps, use a sequence: '<<: [*anchor1, *anchor2]'.
return dyn.InvalidValue, &LocationError{
Loc: d.location(key),
Summary: "duplicate YAML merge key ('<<') is not allowed; to merge multiple maps, use a sequence: '<<: [*anchor1, *anchor2]'",
}
}
merge = val
continue
Expand Down
Loading