Allow for user defined functions

Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
This commit is contained in:
Patrick Van Stee
2020-04-15 21:00:21 -04:00
parent 10d4b7a878
commit 870b38837b
9 changed files with 383 additions and 9 deletions

View File

@@ -11,6 +11,7 @@ import (
"github.com/docker/buildx/build"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/docker/pkg/urlutil"
hcl "github.com/hashicorp/hcl/v2"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/pkg/errors"
)
@@ -73,6 +74,7 @@ type Config struct {
Variables []*Variable `json:"-" hcl:"variable,block"`
Groups []*Group `json:"groups" hcl:"group,block"`
Targets []*Target `json:"targets" hcl:"target,block"`
Remain hcl.Body `json:"-" hcl:",remain"`
}
func mergeConfig(c1, c2 Config) Config {

View File

@@ -5,7 +5,9 @@ import (
"strings"
hcl "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/ext/userfunc"
"github.com/hashicorp/hcl/v2/hclsimple"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/function/stdlib"
@@ -14,7 +16,7 @@ import (
// Collection of generally useful functions in cty-using applications, which
// HCL supports. These functions are available for use in HCL files.
var (
functions = map[string]function.Function{
stdlibFunctions = map[string]function.Function{
"absolute": stdlib.AbsoluteFunc,
"add": stdlib.AddFunc,
"and": stdlib.AndFunc,
@@ -71,6 +73,21 @@ type staticConfig struct {
}
func ParseHCL(dt []byte, fn string) (*Config, error) {
// Decode user defined functions.
file, diags := hclsyntax.ParseConfig(dt, fn, hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
return nil, diags
}
userFunctions, _, diags := userfunc.DecodeUserFunctions(file.Body, "function", func() *hcl.EvalContext {
return &hcl.EvalContext{
Functions: stdlibFunctions,
}
})
if diags.HasErrors() {
return nil, diags
}
var sc staticConfig
// Decode only variable blocks without interpolation.
@@ -78,9 +95,8 @@ func ParseHCL(dt []byte, fn string) (*Config, error) {
return nil, err
}
variables := make(map[string]cty.Value)
// Set all variables to their default value if defined.
variables := make(map[string]cty.Value)
for _, variable := range sc.Variables {
variables[variable.Name] = cty.StringVal(variable.Default)
}
@@ -94,6 +110,14 @@ func ParseHCL(dt []byte, fn string) (*Config, error) {
}
}
functions := make(map[string]function.Function)
for k, v := range stdlibFunctions {
functions[k] = v
}
for k, v := range userFunctions {
functions[k] = v
}
ctx := &hcl.EvalContext{
Variables: variables,
Functions: functions,

View File

@@ -93,6 +93,36 @@ func TestParseHCL(t *testing.T) {
require.Equal(t, "124", c.Targets[0].Args["buildno"])
})
t.Run("WithUserDefinedFunctions", func(t *testing.T) {
dt := []byte(`
function "increment" {
params = [number]
result = number + 1
}
group "default" {
targets = ["webapp"]
}
target "webapp" {
args = {
buildno = "${increment(123)}"
}
}
`)
c, err := ParseHCL(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, "124", c.Targets[0].Args["buildno"])
})
t.Run("WithVariables", func(t *testing.T) {
dt := []byte(`
variable "BUILD_NUMBER" {