-1

I try to template some JSON values in a go function, which basically works like I expect it. But if I try to concatenate the template string with a template function it fails (with different errors, depends on how the template string looks)

For example, I have the following line:

{"APPLICATION_HOST":"http://{{ .NAMESPACE }}-repo.{{ .NAMESPACE }}:3004"}

Templating works and gives following output:

{"APPLICATION_HOST":"http://test-repo.test:3004"}

I now want to encode the value with base64, and tried for example the following:

{"APPLICATION_HOST":"{{ http://{{ .NAMESPACE }}-repo.{{ .NAMESPACE }}:3004 | b64encode }}"}

That gives the error:

"http://" is not a fuction

If I just do something like the following (which looks hideous):

{"APPLICATION_HOST":"{{ printf "http://{{ .NAMESPACE }}-repo.{{ .NAMESPACE }}:3004"|b64encode }}"}

It outputs the following:

{"APPLICATION_HOST":"aHR0cDovL3t7IC5OQU1FU1BBQ0UgfX0tcmVwby57eyAuTkFNRVNQQUNFIH19OjMwMDQ="}

What decodes to:

http://{{ .NAMESPACE }}-repo.{{ .NAMESPACE }}:3004

Template function looks like that currently:

func generateDefinitionJson(filePath string, bodyBytes []byte) (interface{}, error) {
    var (
        tpl             bytes.Buffer
        err             error
        tplInterface, m map[string]interface{}
    )
    funcMap := template.FuncMap{
        // The name "b64encode" is what the function will be called in the template text.
        "b64encode": b64encode,
  }
    // read definition file
    fileContent, err := ioutil.ReadFile(filePath)
    // load definition as template
    t, err := template.New("").Funcs(funcMap).Parse(string(fileContent))
    if err != nil {
        logger.Error("Parsing Template failed: " + err.Error())
    } else {
        // create value map
        err = json.Unmarshal([]byte(bodyEscaped), &m)
        mInterfaceStr := fmt.Sprintf("%v", m)
        if err != nil {
            logger.Error("Failed to create Value Map: " + err.Error())
        } else {
            // execute template mapping
            err = t.Execute(&tpl, m)
            logger.Debug(tpl.String())
            if err != nil {
                logger.Error("Templating failed: " + err.Error())
            }
            // unmarshal template into interface
            err = json.Unmarshal([]byte(tpl.String()), &tplInterface)
            if err != nil {
                logger.Error("Couldn't Unmarshal definition to interface: " + err.Error())
            }
        }
    }
    return tplInterface, err
}

func b64encode(str string) string {
    return base64.StdEncoding.EncodeToString([]byte(str))
}

Does anyone has an idea how to solve that issue?

kostix
  • 51,517
  • 14
  • 93
  • 176
  • 4
    Generating JSON via text/template is a bad idea. Use encoding/json. – Volker Dec 21 '20 at 13:05
  • Or if this is in Helm (not that this example seems to be...), use the [toJson](https://helm.sh/docs/chart_template_guide/function_list/#type-conversion-functions) function. – Jonathan Hall Dec 21 '20 at 13:15
  • @Volker do you have an example for your recommendation, because I checked the encoding/json package documentation and can not see any function which would act as template engine – Kevin Geipel Dec 22 '20 at 06:42
  • encoding/json.Marshal _is_ the template engine with templates being structs or maps. – Volker Dec 22 '20 at 07:55

1 Answers1

3

printf does not do template expansion of {{ .NAMESPACE }}, for example. Instead, it expands %s and related verbs. This means that

{"APPLICATION_HOST":"{{ printf "http://{{ .NAMESPACE }}-repo.{{ .NAMESPACE }}:3004"|b64encode }}"}

should be

{"APPLICATION_HOST":"{{ printf "http://%s-repo.%s:3004" .NAMESPACE .NAMESPACE |b64encode }}"}

But the proper answer is to use proper JSON marshaling instead, so that you're certain that valid JSON is produced, and that any unusual characters are properly escaped, etc.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189