Skip to content

Commit

Permalink
Add base64gzip function support to Packer template
Browse files Browse the repository at this point in the history
  • Loading branch information
pwfee committed Aug 10, 2024
1 parent 199a751 commit e19e954
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
57 changes: 57 additions & 0 deletions hcl2template/function/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package function

import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"

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

// Base64GzipFunc constructs a function that compresses a string with gzip and then encodes the result in
// Base64 encoding.
var Base64GzipFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
// Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
// return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil
// },
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
s := args[0].AsString()

var b bytes.Buffer
gz := gzip.NewWriter(&b)
if _, err := gz.Write([]byte(s)); err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("failed to write gzip raw data: %w", err)
}
if err := gz.Flush(); err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("failed to flush gzip writer: %w", err)
}
if err := gz.Close(); err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("failed to close gzip writer: %w", err)
}
return cty.StringVal(base64.StdEncoding.EncodeToString(b.Bytes())), nil
},
})

// Base64Gzip compresses a string with gzip and then encodes the result in
// Base64 encoding.
//
// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
//
// Strings in the Terraform language are sequences of unicode characters rather
// than bytes, so this function will first encode the characters from the string
// as UTF-8, then apply gzip compression, and then finally apply Base64 encoding.
func Base64Gzip(str cty.Value) (cty.Value, error) {
return Base64GzipFunc.Call([]cty.Value{str})
}
49 changes: 49 additions & 0 deletions hcl2template/function/encoding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package function

import (
"fmt"
"testing"

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

func TestBase64Gzip(t *testing.T) {
tests := []struct {
String cty.Value
Want cty.Value
Err bool
}{
{
cty.StringVal("test"),
cty.StringVal("H4sIAAAAAAAA/ypJLS4BAAAA//8BAAD//wx+f9gEAAAA"),
false,
},
{
cty.StringVal("helloworld"),
cty.StringVal("H4sIAAAAAAAA/8pIzcnJL88vykkBAAAA//8BAAD//60g6/kKAAAA"),
false,
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("base64gzip(%#v)", test.String), func(t *testing.T) {
got, err := Base64Gzip(test.String)

if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}

if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
1 change: 1 addition & 0 deletions hcl2template/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func Functions(basedir string) map[string]function.Function {
"basename": filesystem.BasenameFunc,
"base64decode": encoding.Base64DecodeFunc,
"base64encode": encoding.Base64EncodeFunc,
"base64gzip": pkrfunction.Base64GzipFunc,
"bcrypt": crypto.BcryptFunc,
"can": tryfunc.CanFunc,
"ceil": stdlib.CeilFunc,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
page_title: base64gzip - Functions - Configuration Language
description: The base64encode function compresses the given string with gzip and then
encodes the result in Base64.
---

# `base64gzip` Function

`base64gzip` compresses a string with gzip and then encodes the result in
Base64 encoding.

Packer uses the "standard" Base64 alphabet as defined in
[RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4).

Strings in the Packer language are sequences of unicode characters rather
than bytes, so this function will first encode the characters from the string
as UTF-8, and then apply Base64 encoding to the result.

The Packer language applies Unicode normalization to all strings, and so
passing a string through `base64decode` and then `base64encode` may not yield
the original result exactly.

While we do not recommend manipulating large, raw binary data in the Packer
language, this function can be used to compress reasonably sized text strings
generated within the Packer language. For example, the result of this
function can be used to create a compressed object in Amazon S3 as part of
an S3 website.

## Related Functions

- [`base64decode`](/packer/docs/templates/hcl_templates/functions/encoding/base64decode) performs the opposite operation,
decoding Base64 data and interpreting it as a UTF-8 string.
4 changes: 4 additions & 0 deletions website/data/docs-nav-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@
"title": "base64encode",
"path": "templates/hcl_templates/functions/encoding/base64encode"
},
{
"title": "base64gzip",
"path": "templates/hcl_templates/functions/encoding/base64gzip"
},
{
"title": "csvdecode",
"path": "templates/hcl_templates/functions/encoding/csvdecode"
Expand Down

0 comments on commit e19e954

Please sign in to comment.