Skip to content

Commit

Permalink
hcl2template: add filebase64 function
Browse files Browse the repository at this point in the history
The filebase64 function aims to read and encode a file's contents into
base64.
This is mostly to support reading the content of a file that is not
valid UTF-8, as is the case with the `file` function.
  • Loading branch information
lbajolet-hashicorp committed Nov 5, 2024
1 parent 35d1490 commit da06110
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
40 changes: 40 additions & 0 deletions hcl2template/function/filebase64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package function

import (
"encoding/base64"
"fmt"
"os"
"strings"

"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)

var Filebase64 = function.New(&function.Spec{
Params: []function.Parameter{
function.Parameter{
Name: "path",
Description: "Read a file and encode it as a base64 string",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
path := args[0].AsString()
content, err := os.ReadFile(path)
if err != nil {
return cty.NullVal(cty.String), fmt.Errorf("failed to read file %q: %s", path, err)
}

out := &strings.Builder{}
enc := base64.NewEncoder(base64.StdEncoding, out)
_, err = enc.Write(content)
if err != nil {
return cty.NullVal(cty.String), fmt.Errorf("failed to write file %q as base64: %s", path, err)
}
_ = enc.Close()

return cty.StringVal(out.String()), nil
},
})
62 changes: 62 additions & 0 deletions hcl2template/function/filebase64_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package function

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"
)

func TestFilebase64(t *testing.T) {
tests := []struct {
name string
file string
expectedOutput string
expectError bool
}{
{
"file exists, return base64'd contents, no error",
"./testdata/list.tmpl",
"JXsgZm9yIHggaW4gbGlzdCB+fQotICR7eH0KJXsgZW5kZm9yIH59Cg==",
false,
},
{
"file doesn't exist, return nilval and an error",
"./testdata/no_file",
"",
true,
},
{
"directory passed as arg, should error",
"./testdata",
"",
true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := Filebase64.Call([]cty.Value{
cty.StringVal(tt.file),
})

if tt.expectError && err == nil {
t.Fatal("succeeded; want error")
}

if !tt.expectError && err != nil {
t.Fatalf("unexpected error: %s", err)
}

if err != nil {
return
}

retVal := res.AsString()
diff := cmp.Diff(retVal, tt.expectedOutput)
if diff != "" {
t.Errorf("expected output and returned are different: %s", diff)
}
})
}
}
1 change: 1 addition & 0 deletions hcl2template/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func Functions(basedir string) map[string]function.Function {
"distinct": stdlib.DistinctFunc,
"element": stdlib.ElementFunc,
"file": filesystem.MakeFileFunc(basedir, false),
"filebase64": pkrfunction.Filebase64,
"fileexists": filesystem.MakeFileExistsFunc(basedir),
"fileset": filesystem.MakeFileSetFunc(basedir),
"flatten": stdlib.FlattenFunc,
Expand Down

0 comments on commit da06110

Please sign in to comment.