Merge pull request #3198 from rrjjvv/var-typing-no-value-fix
Consider typed, value-less variables to have `null` value
This commit is contained in:
+73
-28
@@ -423,6 +423,63 @@ func TestHCLNullVariables(t *testing.T) {
|
||||
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["foo"])
|
||||
}
|
||||
|
||||
func TestHCLTypedNullVariables(t *testing.T) {
|
||||
types := []string{
|
||||
"any",
|
||||
"string", "number", "bool",
|
||||
"list(string)", "set(string)", "map(string)",
|
||||
"tuple([string])", "object({val: string})",
|
||||
}
|
||||
for _, varType := range types {
|
||||
tName := fmt.Sprintf("variable typed %q with null default remains null", varType)
|
||||
t.Run(tName, func(t *testing.T) {
|
||||
dt := fmt.Sprintf(`
|
||||
variable "FOO" {
|
||||
type = %s
|
||||
default = null
|
||||
}
|
||||
|
||||
target "default" {
|
||||
args = {
|
||||
foo = equal(FOO, null)
|
||||
}
|
||||
}`, varType)
|
||||
c, err := ParseFile([]byte(dt), "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, "true", *c.Targets[0].Args["foo"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHCLTypedValuelessVariables(t *testing.T) {
|
||||
types := []string{
|
||||
"any",
|
||||
"string", "number", "bool",
|
||||
"list(string)", "set(string)", "map(string)",
|
||||
"tuple([string])", "object({val: string})",
|
||||
}
|
||||
for _, varType := range types {
|
||||
tName := fmt.Sprintf("variable typed %q with no default is null", varType)
|
||||
t.Run(tName, func(t *testing.T) {
|
||||
dt := fmt.Sprintf(`
|
||||
variable "FOO" {
|
||||
type = %s
|
||||
}
|
||||
|
||||
target "default" {
|
||||
args = {
|
||||
foo = equal(FOO, null)
|
||||
}
|
||||
}`, varType)
|
||||
c, err := ParseFile([]byte(dt), "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, "true", *c.Targets[0].Args["foo"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONNullVariables(t *testing.T) {
|
||||
dt := []byte(`{
|
||||
"variable": {
|
||||
@@ -1565,6 +1622,20 @@ target "two" {
|
||||
require.Equal(t, map[string]*string{"b": ptrstr("pre-jkl")}, c.Targets[1].Args)
|
||||
}
|
||||
|
||||
func TestEmptyVariable(t *testing.T) {
|
||||
dt := []byte(`
|
||||
variable "FOO" {}
|
||||
target "default" {
|
||||
args = {
|
||||
foo = equal(FOO, "")
|
||||
}
|
||||
}`)
|
||||
c, err := ParseFile(dt, "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, "true", *c.Targets[0].Args["foo"])
|
||||
}
|
||||
|
||||
func TestEmptyVariableJSON(t *testing.T) {
|
||||
dt := []byte(`{
|
||||
"variable": {
|
||||
@@ -1877,19 +1948,6 @@ func TestTypedVarOverrides(t *testing.T) {
|
||||
override: `"hello"`,
|
||||
wantValue: `"hello"`,
|
||||
},
|
||||
{
|
||||
name: "any",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
wantValue: "[1,2]",
|
||||
},
|
||||
{
|
||||
name: "any never convert to complex types",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
argValue: "length(FOO)",
|
||||
wantErrorMsg: "collection must be a list",
|
||||
},
|
||||
{
|
||||
name: "proper CSV list of strings",
|
||||
varType: "list(string)",
|
||||
@@ -2090,19 +2148,6 @@ func TestTypedVarOverrides_JSON(t *testing.T) {
|
||||
override: `"hello"`,
|
||||
wantValue: "hello",
|
||||
},
|
||||
{
|
||||
name: "any",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
wantValue: "[1,2]",
|
||||
},
|
||||
{
|
||||
name: "any never convert to complex types",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
argValue: "length(FOO)",
|
||||
wantErrorMsg: "collection must be a list",
|
||||
},
|
||||
{
|
||||
name: "list of strings",
|
||||
varType: "list(string)",
|
||||
@@ -2313,6 +2358,7 @@ func TestJSONOverridePriority(t *testing.T) {
|
||||
dt := []byte(`
|
||||
variable "foo" {
|
||||
type = number
|
||||
default = 101
|
||||
}
|
||||
|
||||
target "default" {
|
||||
@@ -2325,8 +2371,7 @@ func TestJSONOverridePriority(t *testing.T) {
|
||||
c, err := ParseFile(dt, "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
// a variable with no value has always resulted in an empty string
|
||||
require.Equal(t, "", *c.Targets[0].Args["bar"])
|
||||
require.Equal(t, "101", *c.Targets[0].Args["bar"])
|
||||
|
||||
t.Setenv("foo_JSON", "42")
|
||||
c, err = ParseFile(dt, "docker-bake.hcl")
|
||||
|
||||
@@ -282,7 +282,7 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
|
||||
}
|
||||
|
||||
var diags hcl.Diagnostics
|
||||
varType := cty.DynamicPseudoType
|
||||
varType, typeSpecified := cty.DynamicPseudoType, false
|
||||
def, ok := p.attrs[name]
|
||||
if !ok {
|
||||
vr, ok := p.vars[name]
|
||||
@@ -295,12 +295,13 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
typeSpecified = !varType.Equals(cty.DynamicPseudoType) || hcl.ExprAsKeyword(vr.Type) == "any"
|
||||
}
|
||||
|
||||
if def == nil {
|
||||
// lack of specified value is considered to have an empty string value,
|
||||
// but any overrides get type checked
|
||||
if _, ok, _ := p.valueHasOverride(name, false); !ok {
|
||||
// Lack of specified value, when untyped is considered to have an empty string value.
|
||||
// A typed variable with no value will result in (typed) nil.
|
||||
if _, ok, _ := p.valueHasOverride(name, false); !ok && !typeSpecified {
|
||||
vv := cty.StringVal("")
|
||||
v = &vv
|
||||
return
|
||||
@@ -322,9 +323,6 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Not entirely true... this doesn't differentiate between a user that specified 'any'
|
||||
// and a user that specified nothing. But the result is the same; both are treated as strings.
|
||||
typeSpecified := !varType.Equals(cty.DynamicPseudoType)
|
||||
envv, hasEnv, jsonEnv := p.valueHasOverride(name, typeSpecified)
|
||||
_, isVar := p.vars[name]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user