Skip to content

Commit

Permalink
feat(templates): add multiline helper functions (#6227)
Browse files Browse the repository at this point in the history
This adds some multiline helper functions to the templates engine. Specifically 'mquote', 'msquote', and 'mindent' which are helper functions which when presented with multiline strings have similar behaviour to their similarly named counterparts. The 'mindent' function takes an additional input after the indent number which indicates the YAML multiline formatter to add if it's a multiline string.

Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
  • Loading branch information
james-d-elliott committed Nov 4, 2023
1 parent c4f5c6a commit 00725ec
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 7 deletions.
41 changes: 40 additions & 1 deletion docs/content/en/reference/guides/templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,47 @@ example: |

Overload for [fileContent](#filecontent) except that tailing newlines will be removed.

Example:
##### secret example

```yaml
example: '{{ secret "/absolute/path/to/file" }}'
```

#### mindent

Similar function to `nindent` except it falls back to `indent` if there are no newlines, and includes the YAML multiline
formatting string provided. Input is in the format of `(int, string, string)`.

##### mindent example

Input:

```yaml
example: {{ secret "/absolute/path/to/file" | mindent 2 "|" | msquote }}
```

Output (with multiple lines):

```yaml
example: |
<content of "/absolute/path/to/file">
```

Output (without multiple lines):

```yaml
example: '<content of "/absolute/path/to/file">'
```

#### mquote

Similar to the `quote` function except it skips quoting for strings with multiple lines.

See the [mindent example](#mindent-example) for an example usage (just replace `msquote` with `mquote`, and the expected
quote char is `"` instead of `'`).

#### msquote

Similar to the `squote` function except it skips quoting for strings with multiple lines.

See the [mindent example](#mindent-example) for an example usage.
15 changes: 15 additions & 0 deletions internal/configuration/provider_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package configuration

import (
"crypto/rsa"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -138,6 +139,20 @@ func TestShouldValidateConfigurationWithFilters(t *testing.T) {
assert.Equal(t, "$plaintext$example-xyz", config.IdentityProviders.OIDC.Clients[1].Secret.String())
assert.Equal(t, "$plaintext$example_secret value", config.IdentityProviders.OIDC.Clients[2].Secret.String())
assert.Equal(t, "$plaintext$abc", config.IdentityProviders.OIDC.Clients[3].Secret.String())

require.Len(t, config.IdentityProviders.OIDC.IssuerPrivateKeys, 1)

key, ok := config.IdentityProviders.OIDC.IssuerPrivateKeys[0].Key.(schema.CryptographicPrivateKey)
assert.True(t, ok)
require.NotNil(t, key)

rsakey, ok := key.(*rsa.PrivateKey)
assert.True(t, ok)
require.NotNil(t, rsakey)

assert.Equal(t, 65537, rsakey.E)
assert.Equal(t, "27171434142509968675194232284375073019792572110439705540328918657232692168643195881620537202636198369160560799743144111431567452741046220953662805104932829188046044673961143220261310008810498023470535975681337666107808278037041152412426963982841905494490761888868583347468199094007084012384588888035364766072411615843478518353414183640511444802956354678240763665865557092671631235272029876735331399857244041249715616453815382050245467939750635216436773618819757152567487060661311335480594478902550197306956880336905504741940598285468339785455485086967213774716099196949673312743795439236046960995348506152278833238987", rsakey.N.String())
assert.Equal(t, "5706925720915661669195242494994016816721008820974450261113990040996811079258641550734801632578349185215910392731806135371706455696484447433162465664729853270266472449716574399604756584391664331493231727196142834947800188400138417427667686333274620887920797982823077799989315356653608060034390741776504814150513570875362236882334931949786678793855564217596234691391113095918532726196507032878006343060796051755555405212832046478322407013172691936979796693050565243392092102513298609204623359016844719592078589959501078387650387089103850347191460557257744984924144972386173794776498508384237037750896668486369884278793", rsakey.D.String())
}

func TestShouldHandleNoAddressMySQLWithHostEnv(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions internal/configuration/test_resources/config.filtered.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ notifier:

identity_providers:
oidc:
issuer_private_keys:
# yamllint disable-line rule:braces
- key: {{ secret "./test_resources/crypto/rsa.2048.pem" | mindent 10 "|" | msquote }}
cors:
allowed_origins:
- https://google.com
Expand Down
48 changes: 42 additions & 6 deletions internal/templates/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ func FuncMap() map[string]any {
"trimPrefix": FuncStringTrimPrefix,
"replace": FuncStringReplace,
"quote": FuncStringQuote,
"mquote": FuncStringQuoteMultiLine(rune(34)),
"sha1sum": FuncHashSum(sha1.New),
"sha256sum": FuncHashSum(sha256.New),
"sha512sum": FuncHashSum(sha512.New),
"squote": FuncStringSQuote,
"msquote": FuncStringQuoteMultiLine(rune(39)),
"now": time.Now,
"b64enc": FuncB64Enc,
"b64dec": FuncB64Dec,
Expand Down Expand Up @@ -80,6 +82,7 @@ func FuncMap() map[string]any {
"empty": FuncEmpty,
"indent": FuncIndent,
"nindent": FuncNewlineIndent,
"mindent": FuncMultilineIndent,
"uuidv4": FuncUUIDv4,
"urlquery": url.QueryEscape,
"urlunquery": url.QueryUnescape,
Expand Down Expand Up @@ -210,32 +213,54 @@ func FuncElemsJoin(sep string, elems any) string {
return strings.Join(strslice(elems), sep)
}

// FuncStringSQuote is a helper function that provides similar functionality to the helm squote func.
func FuncStringSQuote(in ...any) string {
// FuncStringQuote is a helper function that provides similar functionality to the helm quote func.
func FuncStringQuote(in ...any) string {
out := make([]string, 0, len(in))

for _, s := range in {
if s != nil {
out = append(out, fmt.Sprintf("'%s'", strval(s)))
out = append(out, fmt.Sprintf("%q", strval(s)))
}
}

return strings.Join(out, " ")
}

// FuncStringQuote is a helper function that provides similar functionality to the helm quote func.
func FuncStringQuote(in ...any) string {
// FuncStringSQuote is a helper function that provides similar functionality to the helm squote func.
func FuncStringSQuote(in ...any) string {
out := make([]string, 0, len(in))

for _, s := range in {
if s != nil {
out = append(out, fmt.Sprintf("%q", strval(s)))
out = append(out, fmt.Sprintf("'%s'", strval(s)))
}
}

return strings.Join(out, " ")
}

// FuncStringQuoteMultiLine is a helper function that provides similar functionality
// to FuncStringQuote and FuncStringSQuote, however it skips quoting if the string contains multiple lines.
func FuncStringQuoteMultiLine(char rune) func(in ...any) string {
return func(in ...any) string {
out := make([]string, 0, len(in))

for _, s := range in {
if s != nil {
sv := strval(s)

if strings.Contains(sv, "\n") {
out = append(out, sv)
} else {
out = append(out, fmt.Sprintf("%c%s%c", char, sv, char))
}
}
}

return strings.Join(out, " ")
}
}

// FuncIterate is a template function which takes a single uint returning a slice of units from 0 up to that number.
func FuncIterate(count *uint) (out []uint) {
var i uint
Expand Down Expand Up @@ -407,6 +432,17 @@ func FuncNewlineIndent(indent int, value string) string {
return "\n" + FuncIndent(indent, value)
}

// FuncMultilineIndent is a helper function that performs YAML multiline intending with a multiline format input such as
// |, |+, |-, >, >+, >-, etc. This is only true if the value has newline characters otherwise it just returns the same
// output as the indent function.
func FuncMultilineIndent(indent int, multiline, value string) string {
if !strings.Contains(value, "\n") {
return FuncIndent(indent, value)
}

return multiline + "\n" + FuncIndent(indent, value)
}

// FuncUUIDv4 is a helper function that provides similar functionality to the helm uuidv4 func.
func FuncUUIDv4() string {
return uuid.New().String()
Expand Down
56 changes: 56 additions & 0 deletions internal/templates/funcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,62 @@ func TestFuncIndent(t *testing.T) {
}
}

func TestFuncMultiLineIndent(t *testing.T) {
testCases := []struct {
name string
have string
indent int
expected string
}{
{"ShouldIndentZeroMultiLine", "abc\n123", 0, "|\nabc\n123"},
{"ShouldIndentOneMultiLine", "abc\n123", 1, "|\n abc\n 123"},
{"ShouldIndentOneSingleLine", "abc", 1, " abc"},
{"ShouldIndentZeroSingleLine", "abc", 0, "abc"},
}

for _, tc := range testCases {
assert.Equal(t, tc.expected, FuncMultilineIndent(tc.indent, "|", tc.have))
}
}

func TestMultiLineQuote(t *testing.T) {
testCases := []struct {
name string
have string
char rune
expected string
}{
{
"ShouldSQuoteSingleLine",
"abc",
rune(39),
`'abc'`,
},
{
"ShouldQuoteSingleLine",
"abc",
rune(34),
`"abc"`,
},
{
"ShouldNotQuoteLine",
"abc\n123",
rune(39),
"abc\n123",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
quote := FuncStringQuoteMultiLine(tc.char)

actual := quote(tc.have)

assert.Equal(t, tc.expected, actual)
})
}
}

func TestFuncUUIDv4(t *testing.T) {
assert.Len(t, FuncUUIDv4(), 36)
}
Expand Down

0 comments on commit 00725ec

Please sign in to comment.