mirror of https://github.com/databricks/cli.git
Raise an error when double underscore variable reference is used
This commit is contained in:
parent
34a37cf4a8
commit
f0e8b7ea8f
|
@ -0,0 +1,14 @@
|
||||||
|
bundle:
|
||||||
|
name: double_underscore
|
||||||
|
|
||||||
|
variables:
|
||||||
|
double__underscore:
|
||||||
|
description: "This is a variable with a double underscore"
|
||||||
|
default: "default"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
jobs:
|
||||||
|
test_job:
|
||||||
|
name: "test"
|
||||||
|
tasks:
|
||||||
|
- task_key: "test ${var.double__underscore}"
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
>>> $CLI bundle validate
|
||||||
|
Error: incorrect variable name: [${var.double__underscore}]
|
||||||
|
|
||||||
|
Name: double_underscore
|
||||||
|
Target: default
|
||||||
|
Workspace:
|
||||||
|
User: $USERNAME
|
||||||
|
Path: /Workspace/Users/$USERNAME/.bundle/double_underscore/default
|
||||||
|
|
||||||
|
Found 1 error
|
||||||
|
|
||||||
|
Exit code: 1
|
|
@ -0,0 +1 @@
|
||||||
|
trace $CLI bundle validate
|
|
@ -6,7 +6,10 @@ import (
|
||||||
"github.com/databricks/cli/libs/dyn"
|
"github.com/databricks/cli/libs/dyn"
|
||||||
)
|
)
|
||||||
|
|
||||||
var re = regexp.MustCompile(`\$\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\[[0-9]+\])*)*(\[[0-9]+\])*)\}`)
|
var (
|
||||||
|
re = regexp.MustCompile(`\$\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\[[0-9]+\])*)*(\[[0-9]+\])*)\}`)
|
||||||
|
potentialVarRef = regexp.MustCompile(`\$\{[a-zA-Z0-9\.-_]+}`)
|
||||||
|
)
|
||||||
|
|
||||||
// ref represents a variable reference.
|
// ref represents a variable reference.
|
||||||
// It is a string [dyn.Value] contained in a larger [dyn.Value].
|
// It is a string [dyn.Value] contained in a larger [dyn.Value].
|
||||||
|
@ -30,23 +33,32 @@ type ref struct {
|
||||||
// - "${a.b}"
|
// - "${a.b}"
|
||||||
// - "${a.b.c}"
|
// - "${a.b.c}"
|
||||||
// - "${a} ${b} ${c}"
|
// - "${a} ${b} ${c}"
|
||||||
func newRef(v dyn.Value) (ref, bool) {
|
func newRef(v dyn.Value) (ref, ref, bool) {
|
||||||
s, ok := v.AsString()
|
s, ok := v.AsString()
|
||||||
if !ok {
|
if !ok {
|
||||||
return ref{}, false
|
return ref{}, ref{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the string contains any variable references.
|
// Check if the string contains any variable references.
|
||||||
m := re.FindAllStringSubmatch(s, -1)
|
m := re.FindAllStringSubmatch(s, -1)
|
||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
return ref{}, false
|
// Check if the string contains any potential variable references but they are not valid.
|
||||||
|
pm := potentialVarRef.FindAllStringSubmatch(s, -1)
|
||||||
|
if len(pm) > 0 {
|
||||||
|
return ref{}, ref{
|
||||||
|
value: v,
|
||||||
|
str: s,
|
||||||
|
matches: pm,
|
||||||
|
}, false
|
||||||
|
}
|
||||||
|
return ref{}, ref{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref{
|
return ref{
|
||||||
value: v,
|
value: v,
|
||||||
str: s,
|
str: s,
|
||||||
matches: m,
|
matches: m,
|
||||||
}, true
|
}, ref{}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPure returns true if the variable reference contains a single
|
// isPure returns true if the variable reference contains a single
|
||||||
|
@ -75,7 +87,7 @@ func IsPureVariableReference(s string) bool {
|
||||||
// If s is a pure variable reference, this function returns the corresponding
|
// If s is a pure variable reference, this function returns the corresponding
|
||||||
// dyn.Path. Otherwise, it returns false.
|
// dyn.Path. Otherwise, it returns false.
|
||||||
func PureReferenceToPath(s string) (dyn.Path, bool) {
|
func PureReferenceToPath(s string) (dyn.Path, bool) {
|
||||||
ref, ok := newRef(dyn.V(s))
|
ref, _, ok := newRef(dyn.V(s))
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewRefNoString(t *testing.T) {
|
func TestNewRefNoString(t *testing.T) {
|
||||||
_, ok := newRef(dyn.V(1))
|
_, _, ok := newRef(dyn.V(1))
|
||||||
require.False(t, ok, "should not match non-string")
|
require.False(t, ok, "should not match non-string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ func TestNewRefValidPattern(t *testing.T) {
|
||||||
"${helloworld.world-world}": {"helloworld.world-world"},
|
"${helloworld.world-world}": {"helloworld.world-world"},
|
||||||
"${hello-world.world-world}": {"hello-world.world-world"},
|
"${hello-world.world-world}": {"hello-world.world-world"},
|
||||||
} {
|
} {
|
||||||
ref, ok := newRef(dyn.V(in))
|
ref, _, ok := newRef(dyn.V(in))
|
||||||
require.True(t, ok, "should match valid pattern: %s", in)
|
require.True(t, ok, "should match valid pattern: %s", in)
|
||||||
assert.Equal(t, refs, ref.references())
|
assert.Equal(t, refs, ref.references())
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,21 @@ func TestNewRefInvalidPattern(t *testing.T) {
|
||||||
"${a-a.a--a-a.id}", // fails because of -- in the second segment
|
"${a-a.a--a-a.id}", // fails because of -- in the second segment
|
||||||
}
|
}
|
||||||
for _, v := range invalid {
|
for _, v := range invalid {
|
||||||
_, ok := newRef(dyn.V(v))
|
_, pr, ok := newRef(dyn.V(v))
|
||||||
require.False(t, ok, "should not match invalid pattern: %s", v)
|
require.False(t, ok, "should not match invalid pattern: %s", v)
|
||||||
|
require.Empty(t, pr.matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRefInvalidPatternWithDoubleUnderscore(t *testing.T) {
|
||||||
|
invalid := []string{
|
||||||
|
"${hello__world.world_world}",
|
||||||
|
"${hello_world.world__world}",
|
||||||
|
}
|
||||||
|
for _, v := range invalid {
|
||||||
|
_, pr, ok := newRef(dyn.V(v))
|
||||||
|
require.False(t, ok, "should not match invalid pattern: %s", v)
|
||||||
|
require.NotEmpty(t, pr.matches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,12 @@ func (r *resolver) collectVariableReferences() (err error) {
|
||||||
|
|
||||||
// First walk the input to gather all values with a variable reference.
|
// First walk the input to gather all values with a variable reference.
|
||||||
_, err = dyn.Walk(r.in, func(p dyn.Path, v dyn.Value) (dyn.Value, error) {
|
_, err = dyn.Walk(r.in, func(p dyn.Path, v dyn.Value) (dyn.Value, error) {
|
||||||
ref, ok := newRef(v)
|
ref, potentialRef, ok := newRef(v)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if len(potentialRef.matches) > 0 {
|
||||||
|
// If the value contains a potential variable reference, we should skip it.
|
||||||
|
return dyn.InvalidValue, fmt.Errorf("incorrect variable name: %s", potentialRef.matches[0])
|
||||||
|
}
|
||||||
// Skip values without variable references.
|
// Skip values without variable references.
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
@ -206,7 +210,7 @@ func (r *resolver) resolveKey(key string, seen []string) (dyn.Value, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the returned value is a valid variable reference, resolve it.
|
// If the returned value is a valid variable reference, resolve it.
|
||||||
ref, ok := newRef(v)
|
ref, _, ok := newRef(v)
|
||||||
if ok {
|
if ok {
|
||||||
v, err = r.resolveRef(ref, seen)
|
v, err = r.resolveRef(ref, seen)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue