Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bake: merge targets and vars from multiple JSON files #1025

Merged
merged 2 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
bake: merge vars from multiple JSON files
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
  • Loading branch information
crazy-max committed Apr 6, 2022
commit cad7ed68be7af1f69da40cafb229f8ef6ce3fee9
51 changes: 50 additions & 1 deletion bake/hcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ func TestHCLBuiltinVars(t *testing.T) {
require.Equal(t, "test", *c.Targets[0].Dockerfile)
}

func TestCombineHCLAndJSON(t *testing.T) {
func TestCombineHCLAndJSONTargets(t *testing.T) {
c, err := ParseFiles([]File{
{
Name: "docker-bake.hcl",
Expand Down Expand Up @@ -696,3 +696,52 @@ target "b" {
require.Equal(t, ".", *c.Targets[3].Context)
require.Equal(t, "b", *c.Targets[3].Target)
}

func TestCombineHCLAndJSONVars(t *testing.T) {
c, err := ParseFiles([]File{
{
Name: "docker-bake.hcl",
Data: []byte(`
variable "ABC" {
default = "foo"
}
variable "DEF" {
default = ""
}
group "default" {
targets = ["one"]
}
target "one" {
args = {
a = "pre-${ABC}"
}
}
target "two" {
args = {
b = "pre-${DEF}"
}
}`),
},
{
Name: "foo.json",
Data: []byte(`{"variable": {"DEF": {"default": "bar"}}, "target": { "one": { "args": {"a": "pre-${ABC}-${DEF}"}} } }`),
},
{
Name: "bar.json",
Data: []byte(`{"ABC": "ghi", "DEF": "jkl"}`),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it still work when hcl also defines ABC = "old" and foo.json "ABC": "old2"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed I tried with attributes in global scope an doesn't seem to work atm (see last commit)

=== RUN   TestCombineHCLAndJSONAttrs
    hcl_test.go:790: 
        	Error Trace:	hcl_test.go:790
        	Error:      	Not equal: 
        	            	expected: map[string]string{"a":"pre-ghi-jkl"}
        	            	actual  : map[string]string{"a":"pre-foo-jkl"}

I guess it's how merging blocks are handled for attributes in global scope where only the first one is retained atm:

ABC = "foo"
variable "DEF" {
  default = ""
}
group "default" {
  targets = ["one"]
}
target "one" {
  args = {
    a = "pre-${ABC}"
  }
}
target "two" {
  args = {
    b = "pre-${DEF}"
  }
}
{"ABC": "oof", "variable": {"DEF": {"default": "bar"}}, "target": { "one": { "args": {"a": "pre-${ABC}-${DEF}"}} } }
{"ABC": "ghi", "DEF": "jkl"}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it at least work with HCL-only?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with HCL only (see test in last commit) 😞:

=== RUN   TestCombineHCLAttrs
    hcl_test.go:801: 
        	Error Trace:	hcl_test.go:801
        	Error:      	Not equal: 
        	            	expected: map[string]string{"a":"pre-ghi-jkl"}
        	            	actual  : map[string]string{"a":"pre-foo-jkl"}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we missed a case in #541. Changing this test with a global attr FOO = "abc" in first file makes it failed:

func TestHCLMultiFileAttrs(t *testing.T) {
	os.Unsetenv("FOO")
	dt := []byte(`
		FOO = "abc"
		target "app" {
			args = {
				v1 = "pre-${FOO}"
			}
		}
		`)
	dt2 := []byte(`
		FOO="def"
		`)

	c, err := ParseFiles([]File{
		{Data: dt, Name: "c1.hcl"},
		{Data: dt2, Name: "c2.hcl"},
	}, nil)
	require.NoError(t, err)
	require.Equal(t, 1, len(c.Targets))
	require.Equal(t, c.Targets[0].Name, "app")
	require.Equal(t, "pre-def", c.Targets[0].Args["v1"])

	os.Setenv("FOO", "ghi")

	c, err = ParseFiles([]File{
		{Data: dt, Name: "c1.hcl"},
		{Data: dt2, Name: "c2.hcl"},
	}, nil)
	require.NoError(t, err)

	require.Equal(t, 1, len(c.Targets))
	require.Equal(t, c.Targets[0].Name, "app")
	require.Equal(t, "pre-ghi", c.Targets[0].Args["v1"])
}
=== RUN   TestHCLMultiFileAttrs
    hcl_test.go:488: 
        	Error Trace:	hcl_test.go:488
        	Error:      	Not equal: 
        	            	expected: "pre-def"
        	            	actual  : "pre-abc"

Copy link
Member Author

@crazy-max crazy-max Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah JustAttributes skips repeated attributes: https://github.com/hashicorp/hcl/blob/c7ee8b78101c33b4dfed2641d78cf5e9651eabb8/merged.go#L109-L120

We might need to bring in this func in the hclparser to fix that for us wdyt?

},
}, nil)
require.NoError(t, err)

require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"one"}, c.Groups[0].Targets)

require.Equal(t, 2, len(c.Targets))

require.Equal(t, c.Targets[0].Name, "one")
require.Equal(t, map[string]string{"a": "pre-ghi-jkl"}, c.Targets[0].Args)

require.Equal(t, c.Targets[1].Name, "two")
require.Equal(t, map[string]string{"b": "pre-jkl"}, c.Targets[1].Args)
}
10 changes: 8 additions & 2 deletions bake/hclparser/hclparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {

attrs, diags := b.JustAttributes()
if diags.HasErrors() {
if d := removeAttributesDiags(diags, reserved); len(d) > 0 {
if d := removeAttributesDiags(diags, reserved, p.vars); len(d) > 0 {
return d
}
}
Expand Down Expand Up @@ -513,7 +513,7 @@ func setLabel(v reflect.Value, lbl string) int {
return -1
}

func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}) hcl.Diagnostics {
func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}, vars map[string]*variable) hcl.Diagnostics {
var fdiags hcl.Diagnostics
for _, d := range diags {
if fout := func(d *hcl.Diagnostic) bool {
Expand All @@ -529,6 +529,12 @@ func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{})
return true
}
}
for v := range vars {
// Do the same for global variables
if strings.HasPrefix(d.Detail, fmt.Sprintf(`Argument "%s" was already set at `, v)) {
return true
}
}
return false
}(d); !fout {
fdiags = append(fdiags, d)
Expand Down