Skip to content

Commit

Permalink
template: add test with custom function on decode
Browse files Browse the repository at this point in the history
When working on plugins' code, we noticed that in some cases functions
could not be invoked when decoding a value in HCL2 templates.

This is because in some plugins, the interpolation context is part of
the configuration structure, and is also used for interpolating data in
the SDK's Decode function, used to convert over-the-wire data (either in
gob or cty format) into a flat representation of a final config, then to
the config used by the component.

During this step, we zero-out the final config object for HCL2
templates, as we call `Prepare` multiple times for some component types,
typically provisioners and post-processors, and preparation may yield a
final object that cannot re-pass validation.

However, since the configuration object contains the interpolation
context that is passed to `Decode`, it also gets zeroed-out, which
causes any custom functions locally registered to the context to be
lost, and therefore causing Decode to fail.

We don't yet have a robust solution to this problem, but with this
commit, we have a test that reliably reproduces the error so that we can
later validate we have a fix to this.
  • Loading branch information
lbajolet-hashicorp committed Apr 26, 2024
1 parent a4700a2 commit f416485
Showing 1 changed file with 68 additions and 0 deletions.
68 changes: 68 additions & 0 deletions template/config/decode_test.go
Expand Up @@ -4,12 +4,15 @@
package config

import (
"fmt"
"reflect"
"strings"
"testing"
"time"

"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/zclconf/go-cty/cty"
)

func TestDecode(t *testing.T) {
Expand Down Expand Up @@ -207,3 +210,68 @@ func TestDecode_fixerRecommendations(t *testing.T) {
}
}
}

type CustomFuncTestConfig struct {
Name string
ctx interpolate.Context
}

func (tc CustomFuncTestConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return &FlatCustomFuncTestConfig{}
}

type FlatCustomFuncTestConfig struct {
Name string `cty:"name" hcl:"name"`
}

func (fc *FlatCustomFuncTestConfig) HCL2Spec() map[string]hcldec.Spec {
return map[string]hcldec.Spec{
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
}
}

func TestDecode_WithCustomFunc(t *testing.T) {

cases := []struct {
Name string
Input []interface{}
}{
{
Name: "HCL2 template - function should not be wiped",
Input: []interface{}{cty.ObjectVal(
map[string]cty.Value{
"name": cty.StringVal("{{ pre }}"),
},
)},
},
}

for _, tc := range cases {
ctx := interpolate.Context{
Funcs: map[string]interface{}{},
}

preFunc := func() string {
return "YO"
}

ctx.Funcs["pre"] = preFunc

result := CustomFuncTestConfig{
Name: "",
ctx: ctx,
}

err := Decode(&result, &DecodeOpts{
InterpolateContext: &result.ctx,
Interpolate: true,
}, tc.Input...)
if err != nil {
t.Errorf("Decode should succeed, but failed: %s", err)
}

if fmt.Sprintf("%p", preFunc) != fmt.Sprintf("%p", result.ctx.Funcs["pre"]) {
t.Errorf("expected %p to be the same as %p, but was not.", preFunc, result.ctx.Funcs["pre"])
}
}
}

0 comments on commit f416485

Please sign in to comment.